循环与迭代器的性能比较
要决定使用循环还是迭代器,你需要知道哪种实现更快:使用显式 for 循环的 search 函数版本,还是使用迭代器的版本。
我们通过将阿瑟·柯南·道尔爵士的《福尔摩斯探案集》全文加载到一个 String 中,并在其中搜索单词 the 来进行基准测试。以下是分别使用 for 循环和迭代器版本的 search 函数的基准测试结果:
test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
两种实现的性能非常接近!我们不会在这里解释基准测试的代码,因为重点不是要证明两个版本完全等价,而是要大致了解这两种实现在性能上的表现。
要进行更全面的基准测试,你应该使用不同大小的文本作为 contents,使用不同的单词和不同长度的单词作为 query,以及各种其他变体。关键在于:迭代器虽然是一种高层抽象,但编译后生成的代码与你手写底层代码几乎相同。迭代器是 Rust 的零成本抽象(zero-cost abstractions)之一,这意味着使用这种抽象不会带来额外的运行时开销。这类似于 C++ 的最初设计者和实现者 Bjarne Stroustrup 在 2012 年 ETAPS 主题演讲“Foundations of C++“中对零开销的定义:
总的来说,C++ 的实现遵循零开销原则:你不使用的东西,不需要为之付出代价。更进一步:你使用的东西,你无法手写出更好的代码。
在很多情况下,使用迭代器的 Rust 代码会编译成与你手写的汇编代码相同的结果。循环展开(loop unrolling)和消除数组访问的边界检查等优化都会被应用,使得生成的代码极其高效。现在你知道了这一点,就可以放心地使用迭代器和闭包了!它们让代码看起来更高层,但不会因此带来运行时性能损失。
总结
闭包和迭代器是 Rust 受函数式编程语言思想启发而提供的特性。它们使 Rust 能够以低层级的性能清晰地表达高层级的思想。闭包和迭代器的实现方式确保了运行时性能不受影响。这是 Rust 致力于提供零成本抽象这一目标的一部分。
既然我们已经改进了 I/O 项目的表达能力,接下来让我们看看 cargo 的更多功能,这些功能将帮助我们与世界分享项目。