Rust 从 0 到 1- 函数式编程 - 性能比较
我们运行了一个性能测试,用于比较基础的 for 循环控制流和迭代器的性能,通过将阿瑟·柯南·道尔的“福尔摩斯探案集”的全部内容存储到一个 String 类型数据里并寻找其中所有的单词 “the”。以下两种实现函数的性能测试结果:
从结果来看迭代器还要稍微快一点点!这里我们主要是为了让大家对它们在性能方面的比较有个大致的认识,并不是为了进行严格的证明,所以就不对测试的代码进行详细的介绍了。
如果要做更全面的性能测试,我们需要考虑不同长度的文本、不同的单词以及所有其它可能的变量。在这里我们主要想表达的是:迭代器,尽管是一个高级的抽象,但是被编译成了与我们自己使用基础代码进行实现大体一致的性能的代码。迭代器是 Rust 的零成本抽象(zero-cost abstractions)之一,意思是其并不会在运行时引入额外的开销,这与 C++ 语言的发明者,本贾尼·斯特劳斯特卢普(Bjarne Stroustrup)在 《Foundations of C++》(2012) 中所定义的零开销(zero-overhead)类似:
作为另一个例子,我们摘取了部分音频解码器的代码。解码算法使用基于线性预测(linear prediction )的数学计算,来根据之前样本的线性函数预测将来的值。代码使用迭代器链来对作用域中的三个变量进行一些数学计算步骤,三个变量是:buffer-音频数据的切片、coefficients-用于存储系数的数据、qlp_shift-代表位移位数。参考下面的例子:
对音频解码器这类程序通常来说性能很重要。在上面的例子中,我们创建了一个 coefficients 的迭代器,并通过链式调用创建了两个迭代器适配器,最后使用 sum 完成消费。那么,这段 Rust 代码最终将会被编译成什么样呢?截至编写这篇文档的时候,它将被编译成与我们写的的汇编代码一样。对 coefficients 的遍历完全没有用到循环:Rust 知道会迭进 12 次迭代,所以它对循环进行了“展开(unrolls)”。展开(Unrolling)是一种对循环控制代码的优化,它使用重复的代码来替代循环,从而移除了循环控制代码带来的开销。所有的系数(coefficients)都被储存在寄存器中,访问他们非常快,也没有运行时的数组访问边界检查。所有的这些优化使得代码极为高效。所以,请大家放心大胆的使用迭代器和闭包吧!他们使得我们可以按照高级语言的方式进行编码,同时又不会带来额外的运行时性能损失。
总结
闭包和迭代器是 Rust 受函数式编程语言影响所设计的功能。他们对让 Rust 可以使用高级语言的形式来编码,同时又具备低级语言的性能来说贡献很大。闭包和迭代器的实现做到了不会影响程序运行时的性能,这是 Rust 提供零成本抽象努力达到的目标之一。
版权声明: 本文为 InfoQ 作者【山】的原创文章。
原文链接:【http://xie.infoq.cn/article/b1f932dc2a68d419a0340a4c3】。文章转载请联系作者。
评论