3

Rust中的零成本抽象(一)(译)

 3 years ago
source link: https://zhuanlan.zhihu.com/p/109517672
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Rust中的零成本抽象(一)(译)

Zero Cost Abstractions​boats.gitlab.io

零成本抽象对于C++和Rust是非常重要的,因为它们想让程序员以相对较少的努力就能写出有优秀性能的代码。因为这是Rust设计及我工作的基础,我想介绍下零成本抽象是什么。

零成本抽象来自C++,由C++创始人Bjarne Stroustrup定义:

通常,C++的实现遵循零开销原则:你不使用的,你不负担成本。更进一步:你使用的,你也没法更优化。

定义有两个关键:

  • 没有整体开销: 零成本抽象不应该对不使用的程序的产生开销。比如,它不能运行所有的程序、负担沉重的运行时开销,却仅仅只为使用其中一些功能时受益。(译者注:抽象中的所有部分都是你会用到的,你用不到的就不会包含在这个抽象底层中)
  • 优化性能: 零成本抽象生成的机器代码,手写应无法超越。它不能引入可避免的开销。

还必须知道有第三个关键。然而,因为它同样是所有好抽象(无论是否零成本)的关键,所以经常被忽略:

  • 提升程序员体验: 抽象,是为了包装低层代码,使程序员更容易地写出他们所想。零成本抽象(及所有的抽象)必须提供比任何替代抽象更好的体验。

这3点是永远也实现不了的理想状态,特别第3点,与个人偏好没有关系。但是对零成本抽象,第三点实际上特别重要。因为零成本抽象通常要在三者之间权衡。

一方面,它必须至少和手写代码有一样的性能,这点相当清晰。但是另一方面,它也肯定要好于比有额外开销并使用非零成本抽象的代码。这并不意味着我们必须比非零成本抽象更严格,因为性能成本只是一个因素,我们必须在广义上,使程序员承担的所有额外开销都成为“零成本”,这也是值得的。(我认为,Rust的某些设计为了取得更高一点的性能,让用户通过不愉快的方式去编程,试图让人感觉零成本抽象更好。我认为这是一个错误,损害了语言的整体用户体验,可能让一些用户根本无法使用它,从而损害了我们的总体目标。)

(I think to some extent Rust has tried to cheat this by making non-zero cost abstractions actively unpleasant to write, making it feel like the zero cost abstraction is better. I think this is a mistake that hurts the overall UX of the language and probably just keeps some people from using it at all, hurting our overall goals.)

我还要说,实际上实现满足这三个关键的零成本抽象困难地让人难以置信。

Rust确实做了几次出色的工作(所有这些都产生了极高的影响),并且参与了我认为将成为成功案例之一的工作-当然是异步/等待-感觉就像在握着你的手。去年9月,我向一位朋友评论说,我担心自己做的任何事情都不会像我刚刚做的那样好(参考Pin API),我确实感到了。

但是我们很少能取得如此出色的成就,因为它极其困难,并且还涉及很多运气(很多问题可能还没有真正意义上的零成本抽象,但至少在设计约束范围内,之前的历史决策已经被考虑在内)。

为了明确我的意思,我将列出Rust中一些真正很棒的零成本抽象:

  • Ownership and borrowing( 所有权和借用)当然这是最棒的一个,不使用垃圾收集并保证内存和线程安全,是Rust最初也是最大的成功点。
  • Iterator and closure APIs.(迭代和闭包)这是另外一个经典。尽管在某些情况下可能会更好地优化内部迭代,但是您可以在切片上编写map,filter,迭代循环等,并使其优化到与某些手写C相当的程度,这确实令人震惊。
  • Async/await and Futures.(异步/等待和Futures Future API是一个重要的示例,因为Future的早期版本确实很好地实现了零成本抽象的“零成本”部分,但实际上并没有提供足够好的用户体验来推动用户使用它。通过添加补丁以支持异步/等待,跨等待的引用等等,我们开发出了一种我认为确实可以解决用户问题并使Rust更适合编写高性能网络服务的产品。可以参考此文,还是本文作者(withoutboats)的译文.
  • Unsafe and the module boundary.(不安全模式和模块边界)所有这些以及Rust的每一个成功故事的基础都是Unsafe块和私有的概念,这些概念使我们能够投入原始指针操作来构建这些零成本的抽象。如果没有这种真正的基本能力,Rust的出色功能就不可能实现,它打破规则,让系统超越了类型检查器的处理范围。这是零成本抽象——Rust中所有其他零成本抽象的源泉。

在其他领域,我们仍然没有成功找到零成本的抽象概念。一个例子是trait object,它是动态分配多态性的一种解决方案。 (这里要注意,动态调度是目标的一部分,因此虚拟调用并非非零成本。)问题在于对象安全性的概念,以及固定大小或未知大小的类型,可能还有些不好的ergonomics around the coercions,使得trait object变得非常笨拙;通常我不得不使用它们时会很生气。至少在18个月的时间里,我一直想深入研究这个问题空间,但是其他事情始终是优先事项。

是否需要手动优化代码,以及build和release的性能对比,见下文(另外一个人写的)。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK