Rust是否针对计算范围内的循环进行优化?

3
作为一项练习,我正在尝试在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
    }
}

3
请注意,如果你对性能优化感兴趣,通常最好尝试使用迭代器而不是访问切片的索引,因为索引访问需要进行边界检查,会带来一些开销。在可能的情况下,请使用for v in &arr - undefined
2个回答

5

..是范围操作符,它形成一个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
}

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

这基本上与我在编辑之前的回答非常相似,是针对Benjamin Lindley的评论而做出的回应。 - undefined
@JerryCoffin: 很抱歉,我没有看到你之前的回答版本 :/ - undefined

3

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

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


2
我认为OP指的是整个for循环体的多次执行,而你所讨论的(如果我理解错了请纠正)是for循环单次执行的迭代。换句话说,他想知道每次for循环开始时,是否会重新计算范围的边界,即使它们不能改变(因为数组长度不能改变)。请注意,他的for循环嵌套在另一个循环中。 - undefined
@BenjaminLindley 是的,那正是我想表达的意思。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接