系統(tǒng)級編程語言性能大PK D語言成首選
譯文"C/C++已經(jīng)統(tǒng)治系統(tǒng)編程很久,除了ObjectiveC之外語言都無法獲得很高的關(guān)注。有人用多種系統(tǒng)級語言編寫了同樣的地圖生成工具來測試他們的性能,包括D(DMD,LDC,GDC)、Go(GCC-Go,6g)、Haskell(GHC)和Rust。
相比C/C++,這些語言都原生支持了諸如垃圾回收這些高級特性,也因此無一能達到C/C++的運行速度。這其中表現(xiàn)最差的是原生Go語言編譯器6g,只有Clang22%的速度,而表現(xiàn)最好的是基于LLVM的D語言編譯器LDC,達到了79%。
由于原生就使用了LLVM編譯,Rust成為各語言原生編譯器里最快的一個,但也只達到了45%。從結(jié)果來看,D語言一定是首選。由于D語言許多特性都依賴?yán)厥?,如果需要關(guān)閉垃圾回收而又要保持良好的使用體驗,則推薦Rust。" (摘自:Solidot)
Go,Rust,Haskell和D四語言做關(guān)卡基準(zhǔn)測試
作者在為一款游戲制作隨機關(guān)卡生成器,盡管這個游戲是用C++編寫,且是模塊化,但是類似的關(guān)卡生成器卻可以用更高級的語言來編寫。因為C++并不是最好玩和最豐富的語言,所以作者打算選擇其他語言替代。
“如使用特別依賴迭代和條件句的簡單Roguelike關(guān)卡基準(zhǔn),然后粗略模仿他們的真正生成器。”點擊此處獲取代碼。作者認為Haskell語言的是最易讀的版本:
- roomHitRoom Room {rPos=(x,y), rw=w, rh=h} Room {rPos=(x2, y2), rw=w2, rh=h2}
- | (x2 + w2 +1 ) < x || x2 > (x+w+1 ) = False
- | (y2 + h2 +1 ) < y || y2 > (y+h+1 ) = False
- | otherwise = True
檢查新生成的room與原有的room是否有沖突,如果有沖突就放棄(這是強攻關(guān)卡技巧;我們真正的引擎要更為復(fù)雜,但仍然遵循同樣的原則)。大多數(shù)剩余的時間花在了隨機數(shù)字生成上,所以給基準(zhǔn)加點料,使其像常速那樣具備單獨語言的隨機數(shù)字生成器(例如,這些基準(zhǔn)與作者的目標(biāo)相關(guān),而與你的無關(guān)。)。
PS:所有從Haskell分離出的部署現(xiàn)在都使用XorShift PRNG方法,比較起來更合理。
事實上,只有LDC D的使用XorShift,因為DMD的用XorShift運行起來比用C rand()要慢一些。一個部署XorShift PRND的Haskell版本可能是最受歡迎的。
結(jié)果如下 (C 語言版本用于對比)。
- 編譯器 LDC | Clang | GCC | Rust | GDC | DMD | GCCGo | GHC | 6g
- 時間: 0.412| 0.280 | 0.390 | 0.620 | 0.680 | 0.770 | 0.850 | 1.05 | 0.544
- 最快(%): 68% | 100% | 72% | 45% | 41% | 37% | 33% | 27% | 51%
LDC,Rust和Clang所使用的LLVM版本是3.2.GCC,GCCGo使用的是GCC 4.7.3版本,GDC使用的是 GCC 4.6.4版本。Rust 是0.8-pre (9da42dc 2013-07-17 04:16:42 -0700) 版本,GHC是 7.6.2版本,DMD是2.036版本,6g(Go)是1.1.1版本。它們運行的都是03,-opt-level=3可用于Rust,-release可用于DMD,-funbox-strict-fields可用于Haskell。**D現(xiàn)在運行的時候同樣有-inline和-noboundscheck,所以其速度從1.36s增加到了1.59s。
D是目前測試過的最快的非C語言。在這里不得不表揚LDC編譯器的設(shè)計師們。作者非常期待著一年以內(nèi)再次運行這些基準(zhǔn),看看Clang,LLVM D和LLVM Rust運行得怎樣,并對它們做出比較。
Rust,雖然最初比較慢,但是通過XorShift PRNG,其速度已經(jīng)得到大幅提升。但是為了使Rust的句法適應(yīng)語境,所以Rust的靈活性要受一點點影響;要通過堆形分配的向量,不過需要引用myFunc(&mut myVector),接收到它的函數(shù)要在其類型簽名有myAlias:&mut~[myVector] ,和fn myFunc(myAlias:&mut ~[myVector]) {..} 一樣。與C比較則是
- void myFunc(struct MyType myAlias[arrayLength])
- {
- ..
- }
Rust版本看上去有點拜占庭的味道。還沒有用過Rust的人可以看看,這里給出7種指示符: @ (收集垃圾的堆形分配), ~ (獨特的堆形分配), & (借給堆形/堆棧分配的指示符), * (原始的C指示符, 僅限于不安全代碼), 以及前三種符號的變體(事實上,不確定是否真的有&指示符,所以可能總共只有6個)。注意,具有可變值的指示符和沒有可變值的指示符是截然不同的。
作者還用堆棧分配型向量對Rust版本做了基準(zhǔn)測試,但是速度方面的差異不明顯。不過發(fā)現(xiàn)Rust中的堆棧分配型向量目前有點冗長,因為它不允許未初始化的值,所以不得不創(chuàng)建一個對象實例,作者想要一個所有值都被設(shè)置為零的數(shù)列,然后用上述創(chuàng)新來填充向量。希望,不久的將來,Rust也能學(xué)習(xí)Go,將所有數(shù)值自動設(shè)置為零,或者至少提供這樣一個可選項。目前,它看上去就像這樣:
(顯然,這種想法已經(jīng)成為可能(https://news.ycombinator.com/item?id=6094819)。希望下次升級的時候,Rust教程里能將其記錄下來。)
- et emptyr = Room {X:0,Y:0,W:0,H:0,N:0};
- let emptyt = Tile{X:0,Y:0,T:0};
- let emptyl = Lev{TS : [emptyt,..2500] , RS : [emptyr,..100] };
- let mut ls : [Lev, ..100] = [emptyl,..100];
這里面有很多不必要的代碼,如果是使用堆棧分配型數(shù)列較多的大型項目,這就非常累贅。
Go給人的印象很深刻,雖然不如D,但是作為一種相對比較新的語言,已經(jīng)非常不錯了。默認的PRNG在速度方面有缺陷,所以可改用XorShift。分號的自由使用是一種很好的體驗,就作者個人經(jīng)驗而言,它很適合寫命令式代碼。
盡管作者花了很長時間做優(yōu)化,但是他仍然喜歡Haskell的性能,特別是它必須得攜帶一個包含一千萬整數(shù)的向量。遞歸式編寫是一種很棒的改變,而Haskell版本是所測語言中最簡明的。
PS:升級Haskell,用MWC生成器取代Mersenne,速度可達1.05s。
小結(jié)兩點:沒有運行慢的語言,只有優(yōu)化不到位的匯編;如果不需要加密級別的隨機性,那么XorShift是速度性能最卓越的PRNG運算法則。
——原文參考:Benchmarking level generation: Go, Rust, Haskell and D (and now Scala).
接著Solidot上面所說的,C/C++已經(jīng)統(tǒng)治系統(tǒng)編程很久,除了ObjectiveC之外語言都無法獲得很高的關(guān)注!那么為什么說C語言是系統(tǒng)級編程的首選?
下面我們摘自知乎上張泊寧回答的這個問題很有很有見解,引用至此分享給大家:
第一, C 語言編譯出來的代碼執(zhí)行效率高。Java 是編譯出來的是字節(jié)碼而不是計算機可直接讀的指令,執(zhí)行時候還要再翻譯一遍。雖說這個翻譯過程還是很快的,但對于性能要求比較高的系統(tǒng)級軟件仍然是效率優(yōu)先,不能使用類似 Java, C# 編譯出的字節(jié)碼程序。
第二,C語言的指針功能非常強大,一些像樹、表這樣的數(shù)據(jù)結(jié)構(gòu)離開指針可謂寸步難行。而且指針操作非常高效。 但指針操作對于程序員來說很容易在使用中出錯,因此 Java 不支持指針。再加上上面說的效率問題,所以沒有指針的 Java 不能用來編寫系統(tǒng)級應(yīng)用。
第三,使用 C語言編寫的程序可以非常方便地移植到另一套指令系統(tǒng)的計算機上。參見 Unix 和 Linux.
為什么不用效率更高的匯編語言?因為用匯編語言編寫程序極其費時費力,而且不便移植。所以大多數(shù)情況下用的是嵌入式匯編,即高級語言程序內(nèi)部對于時間或空間要求非??量痰囊欢问褂脜R編語言書寫,但整個程序仍以如 C 這樣的高級語言為主。況且匯編語言是跟指令系統(tǒng)緊密聯(lián)系在一起的,不方便移植。