作为练习,我尝试在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
}
}
答案 0 :(得分:4)
至少在使用arr.len()
嵌套在另一个循环中的快速检查中,似乎根本没有为arr.len()
的“调用”生成代码。在生成的代码中,数组的大小只是硬编码到输出中。
换句话说,我不希望你的第二个片段比第一个片段更快地执行。
答案 1 :(得分:2)
..
是一个范围运算符,它形成一个Range<Idx>
对象(或衍生物:RangeFrom
,RangeFull
或RangeTo
)。这些对象只包含索引(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
}
在这种情况下,如果长度固定,则根本没有循环:它已经展开。