循环与迭代器的性能对比
Performance in Loops vs. Iterators
为了决定是使用循环还是迭代器,你需要了解哪种实现更快:带有显式 for 循环版本的 search 函数,还是带有迭代器的版本。
To determine whether to use loops or iterators, you need to know which
implementation is faster: the version of the search function with an explicit
for loop or the version with iterators.
我们运行了一个基准测试,将亚瑟·柯南·道尔爵士的《福尔摩斯探案集》的全部内容加载到一个 String 中,并在内容中寻找单词 the。以下是使用 for 循环版本的 search 和使用迭代器版本的基准测试结果:
We ran a benchmark by loading the entire contents of The Adventures of
Sherlock Holmes by Sir Arthur Conan Doyle into a String and looking for the
word the in the contents. Here are the results of the benchmark on the
version of search using the for loop and the version using iterators:
test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
这两个实现的性能相似!我们在这里不解释基准测试代码,因为重点不是要证明这两个版本是等效的,而是为了对这两个实现性能对比有一个大致的了解。
The two implementations have similar performance! We won’t explain the benchmark code here because the point is not to prove that the two versions are equivalent but to get a general sense of how these two implementations compare performance-wise.
为了进行更全面的基准测试,你应该检查使用各种大小的不同文本作为 contents,不同的单词和不同长度的单词作为 query,以及所有其他各种变化。重点在于:迭代器虽然是一种高级抽象,但会被编译成与你自己编写底层代码大致相同的代码。迭代器是 Rust 的 零成本抽象(zero-cost abstractions)之一,我们的意思是使用该抽象不会带来额外的运行时开销。这类似于 C++ 的最初设计者和实现者 Bjarne Stroustrup 在他 2012 年 ETAPS 主旨演讲《C++ 基础》(Foundations of C++)中对零开销(zero-overhead)的定义:
For a more comprehensive benchmark, you should check using various texts of
various sizes as the contents, different words and words of different lengths
as the query, and all kinds of other variations. The point is this:
Iterators, although a high-level abstraction, get compiled down to roughly the
same code as if you’d written the lower-level code yourself. Iterators are one
of Rust’s zero-cost abstractions, by which we mean that using the abstraction
imposes no additional runtime overhead. This is analogous to how Bjarne
Stroustrup, the original designer and implementor of C++, defines
zero-overhead in his 2012 ETAPS keynote presentation “Foundations of C++”:
通常情况下,C++ 的实现遵循零开销原则:你没用到的东西,你不必为其付出代价。更进一步:你所用到的东西,你自己手工编写的代码也不会做得更好。
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.
在许多情况下,使用迭代器的 Rust 代码会编译成与你手动编写的相同汇编代码。循环展开和消除数组访问的边界检查等优化措施会被应用,使生成的代码极其高效。既然你已经知道了这一点,就可以放心地使用迭代器和闭包了!它们使代码看起来更高级,但并不会为此带来运行时性能损耗。
In many cases, Rust code using iterators compiles to the same assembly you’d write by hand. Optimizations such as loop unrolling and eliminating bounds checking on array access apply and make the resultant code extremely efficient. Now that you know this, you can use iterators and closures without fear! They make code seem like it’s higher level but don’t impose a runtime performance penalty for doing so.
总结
Summary
闭包和迭代器是受函数式编程语言思想启发的 Rust 特性。它们有助于 Rust 以底层性能清晰地表达高级思想。闭包和迭代器的实现方式使得运行时性能不受影响。这是 Rust 努力提供零成本抽象目标的一部分。
Closures and iterators are Rust features inspired by functional programming language ideas. They contribute to Rust’s capability to clearly express high-level ideas at low-level performance. The implementations of closures and iterators are such that runtime performance is not affected. This is part of Rust’s goal to strive to provide zero-cost abstractions.
现在我们已经改进了 I/O 项目的表达能力,让我们来看看 cargo 的更多特性,这些特性将帮助我们将项目与世界分享。
Now that we’ve improved the expressiveness of our I/O project, let’s look at
some more features of cargo that will help us share the project with the
world.