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

时间:2018-07-01 04:01:01

标签: rust compiler-optimization

我碰到了以下观察结果:

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(因为我们{{1 }}),并像第一个代码段一样不断返回take()

Here are the snippets in the Compiler Explorer

1 个答案:

答案 0 :(得分:4)

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

通过添加打印语句,可以使变量的地址对代码可见并很重要:

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

这意味着优化器无法再跳过为该值生成存储位置或操作存储在其中的值。一旦添加了有关动态调度和IO的所有内容,额外的if就不会增加可观的开销。


您的 real 答案将是阅读LLVM源代码。优化是不平凡的,它们适用且出于深奥的原因不适用。