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>
对象(或派生:RangeFrom
、RangeFull
或RangeTo
)。这些对象仅包含索引(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
}
在这种情况下,固定长度,根本没有循环:它已经展开。
作为练习,我正在尝试微优化 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>
对象(或派生:RangeFrom
、RangeFull
或RangeTo
)。这些对象仅包含索引(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
}
在这种情况下,固定长度,根本没有循环:它已经展开。