为什么 Rust 编译器不能优化 Option::take 和"if let",如果你打印值?



我遇到了以下观察结果:

pub fn xx(mut x: Option<usize>) -> usize {
let y = x.take();
//print!("{:?}", x);
if let Some(x) = x {
x
} else {
0
}
}

这段代码(print!被注释掉(被优化为"无":

xorl    %eax, %eax
retq

一旦我取消注释print!,它就无法再优化if let Some(x) = x了。我继续扩展宏(使用rustc +nightly --pretty=expanded -Z unstable-options main.rs(,并将代码最小化到最小的可编译示例:

#![feature(print_internals)]
#![feature(fmt_internals)]
pub fn xx(mut x: Option<usize>) -> usize {
let y = x.take();
std::io::_print(
::std::fmt::Arguments::new_v1_formatted(
&[""],
&[::std::fmt::ArgumentV1::new(&x, ::std::fmt::Debug::fmt)],
&[]
)
);
if let Some(x) = x {
x
} else {
0
}
}

据我所知,我们只引用x一次,它是一个不可变的引用,所以没有什么可以阻止优化器假设它是None的(因为我们take()它(,并且像第一个代码段一样不断返回0

下面是编译器资源管理器中的代码片段

仅允许优化程序对不更改运行时行为的程序进行更改。通过打印Option您更改了运行时行为,因此优化器现在受到更多限制。

通过添加 print 语句,可以使变量的地址对代码可见且重要:

&[::std::fmt::ArgumentV1::new(&x, ::std::fmt::Debug::fmt)],
//                            ^^

这意味着优化程序不能再跳过为值生成内存位置或操作存储在其中的值。一旦你添加了关于动态调度和IO的所有内容,一个额外的if就不会增加明显的开销。


真正的答案是去阅读LLVM源代码。优化并非平凡,它们适用于和不适用于深奥的原因。

最新更新