|
|
@ -395,7 +395,81 @@ println!("{}", val);
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 迭代器的性能
|
|
|
|
## 迭代器的性能
|
|
|
|
@todo
|
|
|
|
|
|
|
|
|
|
|
|
前面提到,要完成集合遍历,既可以使用for循环也可以使用迭代器,那么二者之间该怎么选择呢,性能有多大差距呢?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
理论分析不会有结果,直接测试最为靠谱:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
#![feature(test)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern crate rand;
|
|
|
|
|
|
|
|
extern crate test;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn sum_for(x: &[f64]) -> f64 {
|
|
|
|
|
|
|
|
let mut result: f64 = 0.0;
|
|
|
|
|
|
|
|
for i in 0..x.len() {
|
|
|
|
|
|
|
|
result += x[i];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
result
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn sum_iter(x: &[f64]) -> f64 {
|
|
|
|
|
|
|
|
x.iter().sum::<f64>()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
|
|
mod bench {
|
|
|
|
|
|
|
|
use test::Bencher;
|
|
|
|
|
|
|
|
use rand::{Rng,thread_rng};
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const LEN: usize = 1024*1024;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn rand_array(cnt: u32) -> Vec<f64> {
|
|
|
|
|
|
|
|
let mut rng = thread_rng();
|
|
|
|
|
|
|
|
(0..cnt).map(|_| rng.gen::<f64>()).collect()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
|
|
fn bench_for(b: &mut Bencher) {
|
|
|
|
|
|
|
|
let samples = rand_array(LEN as u32);
|
|
|
|
|
|
|
|
b.iter(|| {
|
|
|
|
|
|
|
|
sum_for(&samples)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
|
|
fn bench_iter(b: &mut Bencher) {
|
|
|
|
|
|
|
|
let samples = rand_array(LEN as u32);
|
|
|
|
|
|
|
|
b.iter(|| {
|
|
|
|
|
|
|
|
sum_iter(&samples)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
上面的代码对比了for循环和迭代器iterator完成同样的求和任务的性能对比,可以看到迭代器还要更快一点。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
|
|
|
test bench::bench_for ... bench: 998,331 ns/iter (+/- 36,250)
|
|
|
|
|
|
|
|
test bench::bench_iter ... bench: 983,858 ns/iter (+/- 44,673)
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
迭代器是 Rust 的 **零成本抽象**(zero-cost abstractions)之一,意味着抽象并不会引入运行时开销,这与 `Bjarne Stroustrup`(C++ 的设计和实现者)在 `Foundations of C++(2012)` 中所定义的 **零开销**(zero-overhead)如出一辙:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
In general, C++ implementations obey the zero-overhead principle: What you don’t use, you don’t pay for.
|
|
|
|
|
|
|
|
And further: What you do use, you couldn’t hand code any better.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
一般来说,C++的实现遵循零开销原则:没有使用时,你不必为其买单。
|
|
|
|
|
|
|
|
更进一步说,需要使用时,你也无法写出更优的代码了。
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
总之,迭代器是 Rust 受函数式语言启发而提供的高级语言特性,可以写出更加简洁、逻辑清晰的代码。编译器还可以通过循环展开(Unrolling)、向量化、消除边界检查等优化手段,使得迭代器和for循环都有极为高效的执行效率。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
所以请放心大胆的使用迭代器,在获得更高的表达力的同时,也不会导致运行时的损失,何乐而不为呢!
|
|
|
|
|
|
|
|
|
|
|
|
## 学习其它方法
|
|
|
|
## 学习其它方法
|
|
|
|
迭代器用的好不好,就在于你是否掌握了它的常用方法,且能活学活用,因此多多看看[标准库](https://doc.rust-lang.org/std/iter/trait.Iterator.html)是有好处的,只有知道有什么方法,在需要的时候你才能知道该用什么,就和算法学习一样。
|
|
|
|
迭代器用的好不好,就在于你是否掌握了它的常用方法,且能活学活用,因此多多看看[标准库](https://doc.rust-lang.org/std/iter/trait.Iterator.html)是有好处的,只有知道有什么方法,在需要的时候你才能知道该用什么,就和算法学习一样。
|
|
|
|