Rust是否针对计算范围优化循环?

时间:2015-10-13 18:20:17

标签: optimization rust micro-optimization

作为练习,我尝试在Rust 1.3.0中微调优化代码。我在数组上有一个循环循环。像这样:

loop {
    for i in 0..arr.len() {
        // something happens here
    }
}

由于数组在Rust中是固定大小的,编译器是否会通过仅评估arr.len()一次并重用该值来优化代码,还是会在顶级循环的每次传递中评估表达式?除了arr.len()之外,问题可以扩展到更多计算繁重的函数而没有副作用。

换句话说,上面的代码是否等同于:

let arr_len = arr.len();

loop {
    for i in 0..arr_len {
        // something happens here
    }
}

2 个答案:

答案 0 :(得分:4)

至少在使用arr.len()嵌套在另一个循环中的快速检查中,似乎根本没有为arr.len()的“调用”生成代码。在生成的代码中,数组的大小只是硬编码到输出中。

换句话说,我不希望你的第二个片段比第一个片段更快地执行。

答案 1 :(得分:2)

..是一个范围运算符,它形成一个Range<Idx>对象(或衍生物:RangeFromRangeFullRangeTo)。这些对象只包含索引(Idx类型),因此您可以放心.len()仅评估一次。

通常,检查LLVM IR是个好主意。如果你有一个合成的例子,你可以很容易地使用操场。对于example

// A black-box prevents optimization, and its calls are easy to spot.
extern {
    fn doit(i: i32) -> ();
}

fn main() {
    let arr = [1, 2, 3, 4, 5];

    for i in 0..arr.len() {
        unsafe { doit(arr[i]); }
    }
}

产生以下功能:

; Function Attrs: uwtable
define internal void @_ZN4main20hd87dea49c835fe43laaE() unnamed_addr #1 {
entry-block:
  tail call void @doit(i32 1)
  tail call void @doit(i32 2)
  tail call void @doit(i32 3)
  tail call void @doit(i32 4)
  tail call void @doit(i32 5)
  ret void
}

在这种情况下,如果长度固定,则根本没有循环:它已经展开。