Rust 会优化计算范围内的循环吗?

Does Rust optimize for loops over calculated ranges?

作为练习,我正在尝试微优化 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
    }
}

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

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

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


一般来说,检查 LLVM IR 是个好主意。如果你有一个综合示例,你可以很容易地使用 playground。对于 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
}

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