Rust 编译器(特别是 rustc
和背后的 LLVM)擅长把复杂的迭代器链转化成高效的底层代码,当你使用迭代器时,编译器会尽量把这些操作“扁平化”,从而避免不必要的中间步骤和开销。
一个简单的例子
假设有一个整数向量,我们想要对每个元素加 1,然后筛选出偶数,最后求和。
使用迭代器链
先用迭代器链来实现这个操作:
fn sum_basic(v: &Vec<i32>) -> i32 {
v.iter().map(|&x| x + 1).filter(|&x| x % 2 == 0).sum()
}
在这个例子中,用了 iter
、map
、filter
和 sum
四个迭代器方法。你可能会担心这么多操作会不会导致性能问题。实际上,编译器非常聪明,它会把这些高层次的迭代器操作转换成一个单一的循环,从而提升性能。
手写循环
为了验证编译器的优化,我们再写一个手动的循环来完成同样的任务:
fn sum_manual(v: &Vec<i32>) -> i32 {
let mut sum = 0;
for &x in v.iter() {
let y = x + 1;
if y % 2 == 0 {
sum += y;
}
}
sum
}
对比一下这两个函数的性能。你会发现,编译器生成的汇编代码与手写循环的代码非常相似,说明编译器成功地进行了优化。
编译器如何优化?
通过查看编译器生成的汇编代码,可以看到一些有趣的细节。编译器会把 map
和 filter
的组合内联到一个单一的循环中,消除了中间的函数调用和临时变量,也就是这样提升了性能。