Rust所宣稱的zero-cost abstractions是怎麼回事?

我一直都認為「零代價抽象」——即抽象的同時不影響執行的效率——是程序設計的理想狀態,然而現有的編程語言大都沒有達到這個狀態。最近聽說Rust宣稱自己是zero-cost abstractions,所以很好奇:

1. 這跟我所理解的「零代價抽象」是不是一個意思?

2. Rust是如何實現這一點的?

3. 為什麼之前的編程語言都沒有做到這一點?

希望能得到巨擘們的耐心解答,謝謝!

------

再詳細解釋一下我說的」零代價抽象「

比如C++為此做的一些事情:

1. inline,const或者macro的使用

2. 編譯器自動地在編譯期間把int x = 1000 + 9; 中的加法計算處理掉,相當於寫int x = 1009;

這些都是為了在」抽象「的同時不損失程序的執行效率而做的事情。

然而你在C++里寫int x = pow(10, 3) + 9; 編譯器並不能幫你把它優化成int x = 1009; 因為這裡pow是個函數。 所以說C++是做不到我想要的」零代價抽象「的。


樓上的關注點都很怪,類似 pow 這樣的只不過是一個編譯器優化而已,根本不是 zero cost abstraction 的表現。

所謂 zero cost abstraction 指的是你在構建一個抽象的時候這個抽象不會造成額外的負擔,典型的對比是 struct 和 Java 的 class。如果Java 的類 A 里有類類型 B 的成員,那麼通過這個 A 類對象訪問 B 成員事實上需要兩次指針訪問,但如果是 Rust 的 struct,你直接把它分配到棧上,那直接就可以訪問到了,就和 C++ 里的 class 一樣。你雖然做出了抽象,但是並沒有為抽象支付成本,和你不抽象直接把東西放一起是一樣的。


pow這件事,已經基本上可以編譯期摺疊了,因為我們有了constexpr function

當然這不是重點,重點是,rust和C++的抽象都有一個特點,那就是所謂的直接映射到硬體的能力,這些是其他語言都不具備的

C可以直接映射到硬體,但是它沒有抽象


C++14

inline constexpr int pow(int x, int y) {
int r=1;
for(int i=0; i&

搞定。


零抽象應該說的是,只用只需要用的,不用不需要的,也就是說,不調用不多餘的東西,它並不說由編譯器生成的代碼,肯定是最好的選擇,不會出現性能損耗。


Rust has a focus on safety and speed. It accomplishes these goals through many 『zero-cost abstractions』, which means that in Rust, abstractions cost as little as possible in order to make them work. The ownership system is a prime example of a zero-cost abstraction. All of the analysis we』ll talk about in this guide is done at compile time. You do not pay any run-time cost for any of these features.

However, this system does have a certain cost: learning curve. Many new users to Rust experience something we like to call 『fighting with the borrow checker』, where the Rust compiler refuses to compile a program that the author thinks is valid. This often happens because the programmer』s mental model of how ownership should work doesn』t match the actual rules that Rust implements. You probably will experience similar things at first. There is good news, however: more experienced Rust developers report that once they work with the rules of the ownership system for a period of time, they fight the borrow checker less and less.

來自rust book。跟你理解的差別很大,或者說,你理解錯zero-cost abstraction的意思了


本來C++就一直可以做到這一點,只要你不濫用的話。當然了,這句話對於使用debug版本發布的團隊無效。

至於題主說道的pow的問題,就算你拒絕優化,在debug下面寫成

int x = FuckAdd&::Result, 9&>::Resu<

也可以直接給出1009。

當然這麼簡單的東西不值得寫成這樣,然而複雜的其他東西呢(譬如boost::xpressive),C++還有辦法,rust直接嗝屁。


問題和答案都在頂著「抽象」的帽子說「優化」。我就來引用一下,不評論:

http://www.stroustrup.com/abstraction-and-machine.pdf by Bjarne Stroustrup

In general, C++ implementations obey the zero-overhead principle: What you don"t use, you don"t pay for [Stroustrup, 1994]. And further: What you do use, you couldn"t hand code any better.


這題干怎麼改成了Rust?這段C++的答案豈不是白寫了,大家隨便看看吧。

這個東西你要讀cpp他爹寫的那本cpp設計與演化。

零開銷抽象的意思是,你把幾個變數包成了一個類,那個類的效率就和「散裝」變數的內存布局,執行效率完全一致,而不會有box/unbox的開銷。如果用模板寫程序,效率和人力手寫一個版本也一模一樣;當採用元編程技術時,也不會增加運行時的反射開銷。

bs說的很明確,萬一當年搞cpp的時候,做出來的效率比c低,那就是政治不正確。倘若cpp不能做一個better c,也就活不到今天了。


我的理解,0代價抽象,是基於「抽象是要付出代價的」,或者說如果按照抽象層面的設計來直接實現,會有一些損耗,而0代價抽象可以(通過「智能識別」)將這個損耗去掉(或大大降低),從某個角度說這也算是性能優化的一部分,不過題主pow的例子跟抽象好像關係不大

我舉個例子,不知道合不合適,比如類似這樣的代碼:

void f() {

int[] a = new int[10];

}

在java的抽象角度來看,a應該是一個引用,相當於在堆上new一個數組,棧上只是一個指針,然後函數結束,這個對象就該被gc銷毀了,而從實現角度看,這個a沒有逃逸,編譯器完全可以將這個小數組直接分配在棧上,省去堆空間操作的開銷,但是對程序員是透明的,沒有違反抽象的設計

也算是一種優化吧,個人理解


1. 你那個pow的例子只要拿個好點的C++98的編譯器,開高點的編譯優化級別,就優化掉了……

2. 在C++98里,自己手寫一個能模板展開的pow也行,雖然有點丑(因為constexpr和普通函數是兩套寫法),本來題主這樣算個整數乘法去調用浮點庫就怪怪的。什麼樣的抽象都不能避免寫程序的人傻逼帶來的性能開銷。

3. 有了C++14,搞這個問題可以直接上constexpr,一個函數同時解決「參數是常量時結果也是常量」和「參數是變數時結果只能運行時求出」兩種情形。


理解有限,但是肯定不是靜態的去算個pow算個fib。

個人認為zero overhead有兩層含義。

  1. 第一層就是經過抽象後數據本身沒有增加,和沒有抽象的內存佔用一致。
  2. 第二層就是,經過抽象後的代碼,經過了層層封裝,在運行時和沒有封裝過的一樣快,就像你所有的邏輯寫在了一個main裡面一樣。

你舉得例子不好,樓上回答的例子也不好。我個人理解太有限可能誤導,邀請 @WBScan 大神來講講。


零開銷抽象不是題主理解的這個,這個是編譯器優化的事……編譯型語言都能實現的

零開銷抽象在cpp里最直觀的實現,是模板……


inline 跟這事沒關係,const 一定條件下被優化是編譯器的事,macro 要在編譯器摺疊結果寫起來非常繞,template 的奇技淫巧還可以,但 constexpr 才是真正用來干這活的。


看樓上所說的答案,不是CTFE(編譯時計算)嗎?

這在D中,很隨意啊、、

tour里的: https://tour.dlang.org/tour/en/gems/compile-time-function-evaluation-ctfe

加上強大模板和mixin, 模板元也很隨意的、、、


因為C++不管是宏還是模板,功能都並不完全,C++11/14也是如此。很多時候不能提供你所期望的構建domain specific工具的能力。

而且C++還有沉重的歷史包袱。它標準上寫了支持,各家編譯器不一定提供了實現。比如某個宏/模板特性在某些編譯器里不支持,而你又依賴於那個特性,你就只能幹瞪眼。


推薦閱讀:

如何設計文法的語義子程序?
寫一個 JSON、XML 或 YAML 的 Parser 的思路是什麼?
你覺得用中文寫程序怎麼樣?
編譯器的詞法分析和語法分析是如何處理C++的模板?
你對基於機器學習的編譯技術有什麼看法?

TAG:編程語言 | 編譯原理 | Rust編程語言 | 編程語言理論 | 程序設計語言設計 |