<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Sung in Blog

               一些技術(shù)文章 & 一些生活雜碎

    C# 性能能趕上編譯型的 C/C++/D 和中間代碼運(yùn)行時(shí)解釋的 java 嗎?

    微軟發(fā)布了 .net 平臺(tái)和 .net 平臺(tái)支持的多種語(yǔ)言,包括 C#,vb.net, 受管 (managed extensions) 的 C++. 它們構(gòu)成的體系能同時(shí)滿足開(kāi)發(fā)出高效的服務(wù)器架構(gòu),和開(kāi)發(fā)部門(mén)對(duì)強(qiáng)壯的多功能的接口的需求.

    和其他眾多程序員一樣,拿新開(kāi)發(fā)語(yǔ)言與日常用到的語(yǔ)言作比較和選擇的時(shí)候,我對(duì)新玩意總有莫名的偏見(jiàn)和懷疑.本篇文章將會(huì)討論 .net 平臺(tái)的優(yōu)點(diǎn),特別是相關(guān)的 C#. 我們拿 C# 和其他開(kāi)發(fā)語(yǔ)言在高性能軟件開(kāi)發(fā)方面,作定量的性能比較.

    因?yàn)?c# 程序編譯運(yùn)行分為兩部分:先編譯成中間代碼,然后是中間代碼的運(yùn)行時(shí)解釋執(zhí)行,所以我們不僅會(huì)把他跟純編譯型的 C/C++/D 語(yǔ)言相比,也會(huì)把他與典型的 ( 中間代碼運(yùn)行時(shí)解釋 ) 的 Java 語(yǔ)言作比較. Java 無(wú)疑是一個(gè)成熟的語(yǔ)言,應(yīng)用在各個(gè)計(jì)算機(jī)領(lǐng)域. Java 程序先被編譯成中間代碼,然后運(yùn)行時(shí)通過(guò) java 虛擬機(jī)解釋成本地機(jī)器碼執(zhí)行.由此看來(lái), .net 平臺(tái),特別是為首的 c# 語(yǔ)言,借鑒了 java 的很多東西,包括語(yǔ)法和運(yùn)行時(shí)支持.

    對(duì)新語(yǔ)言(或者說(shuō)時(shí)語(yǔ)言 + 對(duì)應(yīng)的庫(kù) + 該語(yǔ)言的運(yùn)行時(shí)支持)作完整公平的性能比較算是一個(gè)龐大考驗(yàn).本文對(duì) c# 與 C/C++/D 就開(kāi)發(fā)中都涉及到的基本共通點(diǎn)作初步的性能比較,同時(shí)還包括各個(gè)語(yǔ)言所帶的庫(kù)的性能比較.包括:

    1. 運(yùn)行一次完整的程序(轉(zhuǎn)載執(zhí)行一個(gè)程序的時(shí)間)

    2. 算法和遞歸(計(jì)算圓周率和 Eratorshnes's sieve )

    3. 類型轉(zhuǎn)換( int->float,float->int,int->string.string->int )

    4. 字符串比較

    5. 字符串連接

    6.( 字符串特定符號(hào)統(tǒng)計(jì) )

    通過(guò)上面的一系列比較,將會(huì)揭示 c# 編譯器效力的一些有意思的地方和 .net 運(yùn)行庫(kù)的性能

    背景 :

    雖然作比較的語(yǔ)言都可以算是 C 語(yǔ)言系列,雖然這五種語(yǔ)言在語(yǔ)法上只有或多或少的不同,但在其他方面,它們的差別是巨大的.(見(jiàn)表一, http://www.digitalmars.com/d/comparison.html ) . 我假設(shè)讀者已經(jīng)熟悉 C/C++/Java ,以及一些基本的 C# .對(duì)于 D 語(yǔ)言,不如其他語(yǔ)言那么有名,但你可以找到它的介紹

    D 語(yǔ)言介紹

    D 語(yǔ)言是 Walter Bright 正在開(kāi)發(fā)的一種語(yǔ)言,安裝和學(xué)習(xí)這種語(yǔ)言的開(kāi)銷(xiāo)很小。實(shí)質(zhì)上, D 語(yǔ)言的目標(biāo)是更成功的 C/C++ 語(yǔ)言,改進(jìn)包括可變長(zhǎng)度的數(shù)組,函數(shù)參數(shù)的 [in][out] 屬性,自動(dòng)內(nèi)聯(lián),版本控制,局部函數(shù),內(nèi)建單元測(cè)試,嚴(yán)格的類型檢查等等,包括 Java/C# 中實(shí)現(xiàn)的忽略指針和引用的差別 , 這在 C++ 中只有通過(guò)模板實(shí)現(xiàn)。它同時(shí)支持對(duì)現(xiàn)有 C 函數(shù)和 API 的直接支持。

    它支持許多跟 Java/C# 類似的特性 , 比如垃圾收集。 D 語(yǔ)言跟 C#/Java 的本質(zhì)區(qū)別是 D 語(yǔ)言運(yùn)行時(shí)不需要運(yùn)行時(shí)的虛擬機(jī)支持。 D 語(yǔ)言所支持的所有特性都是在編譯連接時(shí)完成的,所以不需要運(yùn)行時(shí)的額外支持。 D 語(yǔ)言的開(kāi)發(fā)目前已經(jīng)到了 Alpha 版本,將來(lái)的成熟版本在絕大多數(shù)情況下將會(huì)比 Java/C# 高效得多,除非 JIT 優(yōu)化非常明顯.所以我們才把 D 語(yǔ)言加到我們的測(cè)試當(dāng)中。實(shí)際上, Walter 告訴我們, D 完全有能力產(chǎn)生跟 C 代碼運(yùn)行得一樣快的代碼,同時(shí),由于自動(dòng)內(nèi)聯(lián),內(nèi)建數(shù)組,字符串內(nèi)建優(yōu)化,非 \0 的字符串結(jié)束標(biāo)記等特性,他相信在 D 語(yǔ)言正式發(fā)布的時(shí)候,它的性能將會(huì)超過(guò) C 。

    在真正的測(cè)試開(kāi)始以前,我假定性能按照從高到低排序,應(yīng)該是 C/C++,D,C#,Java .我跟其隨后證實(shí)的其他好多人一樣,一開(kāi)始就懷疑 C# 產(chǎn)生高性能代碼的能力.

    性能比較

    我們會(huì)在下面接著介紹的十一種不同情況下測(cè)試性能.測(cè)試平臺(tái)是 2G 512M 的 pentium4 ,操作系統(tǒng)是 Windows XP Pro. 測(cè)試是在一套我自己設(shè)計(jì)的工具下進(jìn)行的,為了符合這篇文章,我理所當(dāng)然地用 C# 寫(xiě)了這個(gè)工具。在沒(méi)有其他干擾進(jìn)程的情況下,對(duì)每個(gè)測(cè)試者運(yùn)行七次,分別去掉最快的一次和最慢的一次后得到的品均值,就是測(cè)試者的成績(jī)。除了下面說(shuō)到的 noop 測(cè)試,所有的測(cè)試通過(guò)高精確度定時(shí)器計(jì)算完成所有內(nèi)循環(huán)所需時(shí)間完成。(譯者注:比如 int->float 測(cè)量中,會(huì)通過(guò)循環(huán)運(yùn)行多個(gè) int->float 的轉(zhuǎn)換,然后測(cè)量完成整個(gè)循環(huán)需要的時(shí)間)。如 Listing1 所示的代碼,就是用 c# 完成的 f2i 測(cè)試。 (C 通過(guò)調(diào)用 win32 api 函數(shù) QueryPerformanceCounter() ; C++ 通過(guò) WinSTL 的 performance_counter;C# 通過(guò) SynSoft.Performance.PerformanceCounter;D 通過(guò) syncsoft.win32.perf.PerfomanceCounte ; Java 通過(guò) System.currentTimeMillis()) 。每個(gè)程序都包含進(jìn)入實(shí)質(zhì)測(cè)試代碼前一段 ” 熱身代碼 ” 來(lái)減少程序初始化 ,catch 等因素帶來(lái)的不利影響。

    編譯器版本號(hào)如下:

    C#:VC#.net v7.00.9466,.NET Framework v1.0.3705;

    D: Digital Mars D compiler Alpha v0.61

    Java: J2KDSE 1.4.1

    C/C++ 在兩個(gè)編譯器下完成,分別是 Digital Mars C/C++ v8.33,STL Port v4.5 和 Inter C/C++ v7.0 ,頭文件和庫(kù)用 VC++ 6.0

    用 Digital Mars 編譯器的原因是:首先它能快速生成高質(zhì)量代碼,而且免費(fèi)。其次是為了和 D 語(yǔ)言作比較,因?yàn)?D 語(yǔ)言用到相同的 C/C++ 連接器和很多 C 庫(kù)。

    為了作徹底的性能比較,所有的編譯器都用了速度最優(yōu)的優(yōu)化方案。 Inter 編譯選項(xiàng)是 -QaxW ,它提供兩種代碼路徑,一種是專門(mén)為 Pentium4+SIMD 優(yōu)化,一種是為其他 cpu 優(yōu)化。具體的代碼運(yùn)行路徑在運(yùn)行時(shí)自動(dòng)選擇。 ( 通過(guò)對(duì)不同 C/C++ 編譯器性能的分析,我認(rèn)為 Inter 編譯器針對(duì)他自己芯片的優(yōu)化非常好,而對(duì)于 Digital Mars 的編譯器編譯出來(lái)的代碼性能,可以代表普遍的 C/C++ 性能 )

    對(duì)這幾種語(yǔ)言作方方面面的比較是不太現(xiàn)實(shí)的,因?yàn)槟承┨囟ǖ墓δ懿](méi)有在這幾種語(yǔ)言中都得到實(shí)現(xiàn)。比如 C 內(nèi)建庫(kù)里沒(méi)有(字符串特定符號(hào)統(tǒng)計(jì))函數(shù)。不過(guò)既然這篇文章的主題是討論 C#, 所做的測(cè)試都是 C# 支持的,如果其他語(yǔ)言沒(méi)有提供對(duì)應(yīng)的功能,我們會(huì)提供一個(gè)自定義的實(shí)現(xiàn)。同時(shí),如果某種語(yǔ)言對(duì)某種功能的內(nèi)建支持非常差的話,我會(huì)考慮提供一個(gè)相對(duì)好一點(diǎn)的自定義支持。 ( 特別是對(duì)于 C/C++ ,至少有一兩個(gè)地方看得出效率不高的內(nèi)建庫(kù)掩蓋了語(yǔ)言本身的真實(shí)性能 )

    簡(jiǎn)而言之,我不會(huì)列出每種測(cè)試在不同語(yǔ)言的具體實(shí)現(xiàn)代碼,不過(guò)在這里,我會(huì)詳細(xì)介紹下面幾種測(cè)試情況 :

    noop.noop 測(cè)試的是程序的裝載時(shí)間 : 一個(gè)空的 main/Main 的執(zhí)行時(shí)間。跟其它測(cè)試不一樣,這個(gè)測(cè)試是在 ptime 工具下完成的 .( 可以從 http://synesis.com.au/r_systools.html)  獲得,它提供了了 unix 環(huán)境下 time 工具類似的功能,同時(shí)它還能多次運(yùn)行目標(biāo)程序來(lái)獲得平均值 ( 在我們的測(cè)試中, C/C++/D 運(yùn)行 200 次 ,C# 運(yùn)行 50 次, java 運(yùn)行 30 次 ). 為了除去某些特定情況造成的影響,我們會(huì)多次測(cè)量 ( 譯者注:指多次運(yùn)行 ptime 程序, ptime 程序自身又會(huì)多次運(yùn)行目標(biāo)程序 ) ,并且取去掉最高值和最低值后的平均值。

    f2i 這個(gè)測(cè)試把 1 個(gè)長(zhǎng)度為 10,000 的雙精度浮點(diǎn)值轉(zhuǎn)換為 32bit 的整數(shù)。從 Listing 1 可以看出,使用偽隨機(jī)數(shù)產(chǎn)生算法可以保證各語(yǔ)言比較相同的浮點(diǎn)數(shù)。這帶來(lái)了兩個(gè)有趣的情況,我們將在查看結(jié)果的時(shí)候予以討論。

    I2f 跟前一個(gè)相反,這個(gè)測(cè)試對(duì)針對(duì)具體某個(gè)函數(shù)進(jìn)行的測(cè)試。程序生成 10000 個(gè)確定的偽隨機(jī)整數(shù),保證在不同語(yǔ)言中被轉(zhuǎn)換的整數(shù)是相同的。

    I2str 把整型轉(zhuǎn)換成成字符串型。整型變量從 0 循環(huán)增加到 5000000 ,每次做一個(gè)整型到字符串型的轉(zhuǎn)換 ( 見(jiàn) listing2).C#/Java 用內(nèi)建函數(shù)完成 ---i.ToString() 和 Integer.Tostring(i),C/D 用傳統(tǒng)的 sprintf() 完成。 C++ 分為兩次完成。第一次用 iostream 的 strstream 類,和預(yù)期的一樣,這種情況下的性能非常差。為了提高效率,我采取了一些措施,所以導(dǎo)致了第二種方案,見(jiàn) i2str2

    Str2i. 把字符串轉(zhuǎn)換成整型。建立一個(gè)字符串?dāng)?shù)組,內(nèi)容是 i2str 轉(zhuǎn)換過(guò)來(lái)的內(nèi)容,然后再通過(guò)語(yǔ)言本身的支持或者庫(kù)函數(shù)把字符串轉(zhuǎn)換回去,計(jì)算轉(zhuǎn)換回去的時(shí)間(見(jiàn) listing3 ) .C/D 用 atoi 完成, c++ 用 iostream 的 strstream 完成, c# 用 int32.parse 完成, java 用 integer.parseint() 完成。

    picalc 迭代和浮點(diǎn)。通過(guò) listing 4 所示的迭代算法計(jì)算圓周率。每種語(yǔ)言的實(shí)現(xiàn)其迭代次數(shù)是 10,000,000 。



    Picalcr. 遞歸。通過(guò) listing5 所示的算法計(jì)算圓周率。對(duì)每種語(yǔ)言,我們會(huì)做 10000 次運(yùn)算,考慮到 java 堆棧在深層地歸時(shí)很容易被耗盡,所以每次運(yùn)算限制在 4500 次遞歸以內(nèi)。


    Sieve. 疊代。通過(guò) Listing 6 所示的迭代算法( Eratosthenes's sieve ( http://www.math.utah.edu/~alfeld/Eratorthenes.html ))得到質(zhì)數(shù)。每種語(yǔ)言的實(shí)現(xiàn)其迭代次數(shù)是 10,000,000 。


    Strcat. 字符串連接 --- 通常是大量運(yùn)行時(shí)開(kāi)銷(xiāo)的根源,也是導(dǎo)致程序低效的潛在領(lǐng)域。(我以前做過(guò)一個(gè)對(duì)性能敏感的項(xiàng)目,通過(guò)改善效率底下的字符串連接,我獲得了 10% 的整體性能提升) . 測(cè)試通過(guò)構(gòu)造四種不同類型的變量 :short,int, double 和一個(gè) string ,然后把它們連接成一個(gè) string 變量完成,連接時(shí)添加用分號(hào)分割.在連接的時(shí)候采用四種不同的連接方

    法 :1) 默認(rèn)連接方法 2) 故意構(gòu)造出來(lái)的導(dǎo)致效率低下的連接方法 3)C 語(yǔ)言種典型的 printf/Format 格式,需要重新分配空間 4) 為提高性能而進(jìn)行的硬編碼 . 代碼摘錄見(jiàn) Listing7



    Strswtch. 通過(guò)級(jí)聯(lián) switch 進(jìn)行字符串比較 . 通過(guò)語(yǔ)言支持的 switch 語(yǔ)句進(jìn)行比較或者通過(guò)一些列的 if-else 語(yǔ)句模擬( Listing 8 ).這項(xiàng)測(cè)試是為了檢驗(yàn) c# 的字符串處理功能是否如他所聲稱的那樣高效.測(cè)試分成兩部分 : 首先是進(jìn)行字符串之間的賦值,也就是同一身份的比較(譯者注:也就是字符串內(nèi)部的指針直接復(fù)制,不涉及到字符串內(nèi)容的拷貝).其次是通過(guò)字符串的拷貝后進(jìn)行比較,這樣就避免了同一身份比較而是進(jìn)行進(jìn)行字符串之間的字面比較.每種語(yǔ)言上都進(jìn)行 850000 次疊代,因?yàn)槌^(guò)這個(gè)次數(shù)后會(huì)導(dǎo)致 java vm 內(nèi)存耗盡.

    strtok 字符串分段。字符串的運(yùn)行效率也極低,有時(shí)甚至引發(fā)嚴(yán)重的運(yùn)行開(kāi)銷(xiāo)。這個(gè)測(cè)試( Listing 9)讀取一個(gè)文本文件的全部?jī)?nèi)容至一個(gè)字符串,然后拆分成段。第一個(gè)變量用字符分隔符:‘;'分隔字符串,并忽略所有的空白。第二個(gè)變量也用分號(hào)分隔,但保留了空白。第三個(gè)和第四個(gè)變量用字符串分隔符:<->,并相應(yīng)的忽略和保留了空白處。C里僅第二個(gè)變量用了strtok(),C++四個(gè)變量都用STLSoft的string_tokeniser模板,但各自正確給參數(shù)賦值。C#第一個(gè)變量用System.String.Split(),2至4變量用SynSoft.Text.Tokeniser.Tokenise();Java用java.util.StringTokenizer,僅支持變量2和4(注:我選用STLSoft的string_tokeniser模板不是我要為STLSoft作宣傳,而是測(cè)試表明它比Boost的快2.5倍,而B(niǎo)oost是大多數(shù)開(kāi)發(fā)人員在查找?guī)鞎r(shí)的首選)。


    結(jié)果

    在 noop測(cè)試中,表2列出了以微秒(us)為單位裝載一個(gè)空操作的平均開(kāi)銷(xiāo)。顯然C,C++,D的差別不合邏輯,每一個(gè)大約是5-6毫秒,而Intel確低至2ms。我所想到的唯一可能是這個(gè)編譯器優(yōu)化了所有的C實(shí)時(shí)運(yùn)行庫(kù)的初始化工作。如果是這樣,這個(gè)性能在其他非試驗(yàn)性即實(shí)際應(yīng)用中不能體現(xiàn)出來(lái),因?yàn)樗辽傩枰恍〤 … ,這有待深入研究。


    C#用了大約70ms,Java約0.2秒。C#和Java的裝載清晰的表明其實(shí)時(shí)開(kāi)銷(xiāo)在于它們的實(shí)時(shí)運(yùn)行的結(jié)構(gòu)基礎(chǔ)(VM和支持DLLs)。但是除了大規(guī)模的命令行的商業(yè)化批處理,對(duì)極大型系統(tǒng)(上百萬(wàn)條代碼)或CGI的基礎(chǔ)構(gòu)建,啟動(dòng)開(kāi)銷(xiāo)在大多數(shù)情況下不重要。重荷服務(wù)器允許在數(shù)秒內(nèi)啟動(dòng),所以語(yǔ)言間200ms的差異就微乎其微了。


    其余的結(jié)果分三部分顯示。圖 2和3分別展示了字符串連接和分段的結(jié)果。圖1包含了其余全部的測(cè)試結(jié)果,我們現(xiàn)在先來(lái)看看它。



    圖 1中,對(duì)各測(cè)試項(xiàng),C,C++,D,Java的結(jié)果均以其相應(yīng)測(cè)試項(xiàng)C#運(yùn)行時(shí)間的百分比顯示。因此,在100%線上下波動(dòng)的結(jié)果說(shuō)明與C#性能相當(dāng)。高百分比說(shuō)明C#性能較優(yōu),而低百分比表明C#性能相對(duì)較差。

    f2i 拋開(kāi)Intel不談,可以看出C#在浮點(diǎn)到整型的轉(zhuǎn)換中效率遠(yuǎn)遠(yuǎn)高于其余四個(gè),僅是C,C++, 和D開(kāi)銷(xiāo)的2/3,是Java的2/5。在我讓它們使用相同的隨機(jī)數(shù)發(fā)生器前,不同語(yǔ)言的性能差異極大,這說(shuō)明轉(zhuǎn)換速度非常依賴具體的浮點(diǎn)數(shù)值,當(dāng)然這是后見(jiàn)之明了。為了確保每個(gè)語(yǔ)言做相同的轉(zhuǎn)換,程序計(jì)算轉(zhuǎn)換值的總和,這也有助于任何過(guò)度的優(yōu)化去除整個(gè)循環(huán)。可以得知這種轉(zhuǎn)化在語(yǔ)言間正確度不同。每種語(yǔ)言的計(jì)算總和迭代10,000次的結(jié)果在表3中列處。它們表明(雖然無(wú)法證實(shí))C,C++,和D是最正確的,Java其次,C#最差。C#犧牲正確性和降低處理器浮點(diǎn)精度(參見(jiàn) 引用)換取優(yōu)越性能是令人信服的,但事實(shí)上做此判斷前我還需要更多的證據(jù)。至于Intel,說(shuō)它的性能沒(méi)有什么更卓越的也是公平的。


    5個(gè)語(yǔ)言這里效率相當(dāng)。另4種性能比C#低5%以內(nèi)。唯一叫人感興趣的是這四個(gè)語(yǔ)言都是性能比C#略好。這表明C#確實(shí)效率低,雖然在這方面只低一點(diǎn)。這與從整型到浮點(diǎn)型轉(zhuǎn)換的結(jié)果相反。同樣,值得我們注意的是,雖然Java在浮點(diǎn)到整型的轉(zhuǎn)換中性能很低,但在整型到浮點(diǎn)的轉(zhuǎn)換中卻是最好的,比C#高10%。對(duì)f2i,Intel表現(xiàn)極佳,其C和C++分別只用了C#時(shí)間的10%和23%。

    i2str 整型到字符型的轉(zhuǎn)換。 C#比C和D的printf略好。比C++的以iostream為基礎(chǔ)的轉(zhuǎn)換效率要高。它是C#運(yùn)行時(shí)間的4倍(對(duì)Intel 和VC6 STL/CRT是5倍)。但Java性能出色,用了C#2/5的時(shí)間。因?yàn)镃++的效率低下,第二個(gè)變量使用STLSoft的 integer_to_string<> 模板函數(shù)。它在很多編譯器上比C和C++庫(kù)的所有可利用的轉(zhuǎn)換機(jī)制要快。在這種情況下,性能比C#高10%,比C和D高約20%。但是因?yàn)镮ntel編譯器的優(yōu)化影響, integer_to_string<> 似乎找到了其完美搭檔:其時(shí)間比C#低30%,是第三個(gè)速度超過(guò)Java的,比Iostream快17倍以上。認(rèn)為它很可能已經(jīng)接近了轉(zhuǎn)換效率的上限也是合理的。

    str2i 字符型到整型的轉(zhuǎn)化結(jié)果與前面大相徑庭。 C和D用atoi比C#快約5-8倍,比Java快3倍,Java比C#效率高得多。但是這四個(gè)比C++以iostream為基礎(chǔ)的轉(zhuǎn)換都快。C++用 Digital Mars/STLPor運(yùn)行時(shí)間t 是 C#的2.5倍,用 Intel和VC6 CRT/STL 是 10倍。這是iostream的另一個(gè)致命弱點(diǎn)。但因此說(shuō)C++效率低下未免有失公允(實(shí)際上,我正在為 STLSoft 庫(kù)寫(xiě)字符至整型的轉(zhuǎn)換,以和它 integer_to_string<> 的杰出轉(zhuǎn)換性能相匹配。最初的結(jié)果表明它將優(yōu)于 C的atoi,所以這個(gè)轉(zhuǎn)換中C++也是優(yōu)于C的。我希望能在本文刊發(fā)前完成此轉(zhuǎn)換。請(qǐng)參閱 http://stlsoft.org/conversion_library.html 。 )

    picalc 在這里各語(yǔ)言性能表現(xiàn)相近。 C#和Java幾乎相同,比C,C++效率高上10%。C#的性能可以這樣解釋:它的浮點(diǎn)數(shù)操作效率高,這可以從f2I看出。但這不能理解Java的表現(xiàn)。我們可以認(rèn)為C#和Java都能較好的優(yōu)化循環(huán),它涉及函數(shù)調(diào)用,但這有賴于深入研究。既然性能相差10%以內(nèi),我們只能得出這樣的結(jié)論:各語(yǔ)言在循環(huán)結(jié)構(gòu)性能方面差異不顯著。有趣的是,Intel的優(yōu)化這里沒(méi)有實(shí)際效果,可能是因?yàn)镻i值計(jì)算太簡(jiǎn)單了吧。

    Picalcr : 這里的結(jié)果可以更加確定一點(diǎn), C , C++ , C# 和 D 語(yǔ)言的性能偏差在 2% 之內(nèi),我們可以公平地說(shuō)它們對(duì)于遞歸執(zhí)行的性能是一樣的。 Java 卻是花費(fèi)比其他四種語(yǔ)言多 20% 的時(shí)間,加上 JVM ( Java 虛擬機(jī))在超過(guò) 4500 次遞歸時(shí)堆棧耗竭,可以明顯地從速度和內(nèi)存消耗方面得出 Java 處理遞歸不是很好。 Intel 的優(yōu)化能力在這里有明顯的作用(讓人印象深刻的 10% ),但我在這里并沒(méi)有詳細(xì)分析這一點(diǎn)。

    Sieve: 相對(duì)簡(jiǎn)單的計(jì)算質(zhì)數(shù)的算法(僅包含迭代和數(shù)組訪問(wèn))說(shuō)明 C , C++ , C# 和 D 語(yǔ)言在這方面事實(shí)上是一樣的(僅 0.5% 的偏差),但 Java 的性能低了 6% 。我認(rèn)為這中間大有文章, C , C++ 代碼不進(jìn)行數(shù)組邊界安全檢查,而 C# 和 Java 進(jìn)行數(shù)組邊界檢查。我對(duì)這種結(jié)果的解釋是: C# 和 D 語(yǔ)言在 FOR 循環(huán)內(nèi)能根據(jù)對(duì)數(shù)組邊界的條件測(cè)試對(duì)邊界檢查進(jìn)行優(yōu)化,而 Java 不行。由于 C# 是新興語(yǔ)言, D 語(yǔ)言還沒(méi)發(fā)行,這就讓人不那么失望了。即使這不是為 Java 辯解,也不會(huì)讓人印象深刻了。再說(shuō), Intel 優(yōu)化起了明顯的作用——增長(zhǎng) 5% 性能,正如 picalcr 例子,這相對(duì)于其他語(yǔ)言更讓人印象深刻。

    Strswtch : 這個(gè)測(cè)試對(duì)于 C# 語(yǔ)言不是很好。即使是 C++ 低效的字符串類操作符 = = 都比 C# 的性能快 2.5 倍 (Digital Mars) 到 5 倍( Intel VC6 STL/CRT )。 .NET 環(huán)境下字符串是內(nèi)部處理的,這表示字符串存儲(chǔ)在全局離散表中,副本可以從內(nèi)存中消除,等值測(cè)試是以一致性檢查的方式進(jìn)行的 ( 只要兩個(gè)參數(shù)都是在內(nèi)部 ) 。這明顯表示內(nèi)部修復(fù)機(jī)制的效率嚴(yán)重低下或者一致性檢查沒(méi)有建立 C# 的 ”string-swatching” 機(jī)制性。 C# 的這種嚴(yán)重低效率更讓人傾向于后者,因?yàn)橄氩坏絺€(gè)好的理由解釋為什么會(huì)這樣。正如例 8 所示,變量 1 的字符串實(shí)例是文字的,都在內(nèi)部(事實(shí)上,這里有個(gè)限制沒(méi)有在例 8 中表示出來(lái),在變量 1 的預(yù)循環(huán)中用來(lái)驗(yàn)證參數(shù)是真正在內(nèi)部的)。

    當(dāng)在變量 2 中假裝進(jìn)行一致性檢查,發(fā)現(xiàn)僅 Java 的性能有明顯的提高,從 2% 到令人印象深刻的 30% 。很明顯 Java 在使用一致性檢查所取的性能是不切實(shí)際的,因?yàn)樵谡嬲某绦蛑胁荒芟拗谱址际俏淖值摹5@可用來(lái)說(shuō)明支持內(nèi)部處理和提供可編程實(shí)現(xiàn)訪問(wèn)這種機(jī)制的語(yǔ)言的一種性能。諷刺的是, C# 作為這五種語(yǔ)言之一,它的性能是最差的。

    Strcat : 表 2 以微秒( μs )顯示了五種語(yǔ)言每一種在這個(gè)例子中的四個(gè)變量的花費(fèi)時(shí)間。本文沒(méi)有足夠的篇幅來(lái)細(xì)節(jié)描述 17 執(zhí)行代碼,但盡量保持例子 7 的四個(gè)變量的真實(shí)面貌。變量 1 和 2 在所有語(yǔ)言執(zhí)行代碼中涉及以堆棧為基礎(chǔ)的字符串內(nèi)存,同時(shí) C , D 語(yǔ)言在第三個(gè)變量中、 C , C++ , D 語(yǔ)言在第四個(gè)變量中用結(jié)構(gòu)內(nèi)存,這在某種程度說(shuō)明他們的性能優(yōu)于其他變量和語(yǔ)言的執(zhí)行代碼。本例子的 C , C++ 的討論適合于 Digital Mars 版本,因?yàn)楹苊黠@的 Intel 所帶來(lái)的性能提高被 Visual C++ 運(yùn)行時(shí)刻庫(kù)的效率(非期望)所抵消,事實(shí)上 Intel 的每種測(cè)試的性能都比 Digital Mars 的差。

    第一種測(cè)試是以默認(rèn)的方式執(zhí)行的,很顯然 C# 和 D 語(yǔ)言的性能都好于其它語(yǔ)言。很奇怪的是 Java 的性能最差,因?yàn)?Java 被認(rèn)為有能力在一條語(yǔ)句中把字符串序列解釋成字符串構(gòu)件格式(在變量 4 中手工轉(zhuǎn)換)。

    第二種測(cè)試,眾所周知它的格式不好,透露了很多東西。它的劣性根據(jù)語(yǔ)言而不同,對(duì)于 C# , D 和 Java ,包含在連續(xù)的表達(dá)式中連接每一項(xiàng),而不是象第一個(gè)變量在一個(gè)表達(dá)式中。對(duì)于 C ,它簡(jiǎn)單地省略了在開(kāi)始多項(xiàng)連接重新分配之前的的內(nèi)存請(qǐng)求。對(duì)于 C++ ,它使用 strstream 和插入操作符而不是使用 str::string 和 + ()操作符。結(jié)果顯示所有語(yǔ)言不同程度地降低性能。 C 內(nèi)存分配器的附加輪詢看起來(lái)沒(méi)有起多大作用,可能因?yàn)闆](méi)有競(jìng)爭(zhēng)線程造成內(nèi)存碎片,所以消耗同樣的內(nèi)存塊。 C++ 改變成 iostreams 使代碼更緊湊,但沒(méi)有轉(zhuǎn)變數(shù)字部分,它們僅被插入到流當(dāng)中,所預(yù)計(jì)的執(zhí)行結(jié)果驗(yàn)證了這一點(diǎn)。 D 因?yàn)檫@個(gè)改變降低性能許多( 32% ), Java 更多( 60% )。很奇怪的是 C# 性能在這里僅降低很小,少于 7% 。這讓人印象非常深刻,意味著可能在 .NET 的開(kāi)發(fā)環(huán)境下因?yàn)?Java 字符串連接而形成的代碼回查的警覺(jué)本能會(huì)衰退。

    第三種測(cè)試使用 printf()/Format() 的變量不足為奇。通過(guò)使用部分 / 全部的幀內(nèi)存, C , C++ 和 D 達(dá)到很好的性能。奇怪的是 C# 的性能僅是 22% ,簡(jiǎn)直不值一提。 Java 根本沒(méi)有這個(gè)便利功能。(以個(gè)人的觀點(diǎn)也是不值一提的) C#和D語(yǔ)言都能夠在封閉的循環(huán)中,依照數(shù)組邊界測(cè)試情況,優(yōu)化他們的越界檢查。

    最大的 測(cè)試 -硬編碼的性能-這個(gè)是很有趣的問(wèn)題,因?yàn)?C++還是能提供超級(jí)的性能,如果能夠依照問(wèn)題的精確領(lǐng)域來(lái)編碼,即使別的語(yǔ)言(C#,java)使用默認(rèn)的或者性能優(yōu)化的組件或者理念也是如此。在C#和java上使用他們各自的StringBuilders能夠提供真實(shí)的效果,達(dá)到他們?cè)谶@個(gè)集合中的最佳性能。然而,C#能發(fā)揮最佳效率的機(jī)制,還是不能與C/C++、D語(yǔ)言相比。更糟糕的是,java的最佳性能都比別的語(yǔ)言的最差性能要可憐,這是相當(dāng)可悲的。

    總之,我們說(shuō)對(duì)底層直接操作的語(yǔ)言垂手可得地贏得那些轉(zhuǎn)化率工作得很好的或者完全地在中間代碼中,并且很顯然地能夠提供良好性能的語(yǔ)言用法對(duì)java是很重要的而對(duì)C#是不太重要的。請(qǐng)注意這一點(diǎn)。

    Strtok . Table3顯示了五種語(yǔ)言的每一個(gè)環(huán)節(jié)總共的時(shí)間(單位是毫秒ms)。同時(shí)他們的空間占用在這篇文章中并沒(méi)有提及,但是所有語(yǔ)言的實(shí)現(xiàn)都是非常直接的并且很明顯很接近在Listing9中列出的四個(gè)問(wèn)題的本質(zhì)。與strcat一樣,基于同樣的原因,使用Intel編譯器的結(jié)果沒(méi)有討論。

    第一個(gè)問(wèn)題――分割字符,保留空格――顯示C++版本是明顯的勝利者,D語(yǔ)言要慢20%,C# 要3倍的時(shí)間。顯然,這個(gè)與庫(kù)的特征的關(guān)系比語(yǔ)言的更明顯,

    并且我使用 tokenizer比著名的Boost更多一些,那樣的話,D語(yǔ)言要比C++快2倍,并且C#只慢20%。雖然如此,STLSoft的tokenizer是免費(fèi)得到,我想我們應(yīng)該把這個(gè)作為C++的一個(gè)勝利。(其實(shí),template在這一點(diǎn)上的使用STL的basic_string作為他的值類型,這個(gè)并不因?yàn)樗牧己眯阅芏劽@可能引起爭(zhēng)論-我并沒(méi)有使用一個(gè)更有效率的string,就像STLSoft的frame_string.總之,我認(rèn)為這是個(gè)公平比較)。

    第二個(gè)問(wèn)題--分割字符,去掉空格--包含所有語(yǔ)言的實(shí)現(xiàn)版本。自然地,C語(yǔ)言贏得了比賽,它使用了strtok()函數(shù),這個(gè)函數(shù)在創(chuàng)建tokens時(shí)并不分配內(nèi)存并且直接寫(xiě)終結(jié)符NULL到tokenized string的結(jié)尾。盡管有這些不利條件,C++的性能還是很好的,時(shí)間是C的221%,比較好的是D語(yǔ)言,432%。

    C#和java就很差了,分別是746%和957%。這簡(jiǎn)直是不敢相信的,C#和java運(yùn)行的時(shí)間是C++的3.4倍和4.3倍,這三種語(yǔ)言都為tokens分配了內(nèi)存。我相信這個(gè)是STL模型在處理iterable sequences時(shí)比passing around arrays更有效率的很好的例子。(請(qǐng)注意,我們有高度的信心這是場(chǎng)公平的關(guān)于C++,C#和D語(yǔ)言的比較,我寫(xiě)了三種tokerizer的實(shí)現(xiàn),都是公開(kāi)的,可得到的.)

    第三個(gè)問(wèn)題--分割句子,保留空格--顯示了兩件事情。第一,在這三種語(yǔ)言在實(shí)現(xiàn)這個(gè)程序的代價(jià)比實(shí)現(xiàn)單個(gè)字符更昂貴。第二,我們發(fā)現(xiàn)D語(yǔ)言取代了C++的最佳性能的地位,領(lǐng)先5%左右。C#繼續(xù)被甩下,大概是另外兩種語(yǔ)言的1.8倍時(shí)間左右。

    第四個(gè)問(wèn)題--分割句子,去掉空格--沒(méi)有任何令人意外的,C++和D擁有大概一致的性能。C#比較不錯(cuò)(大概2倍的性能差距)java更慢(差不多3倍時(shí)間)。對(duì)于C++,C#和D來(lái)說(shuō)去掉空格比不去掉空格導(dǎo)致一小點(diǎn)的性能損失。很明顯的,在C#中,這么做比其他兩個(gè)的損失更大一點(diǎn)。很有價(jià)值的是,這些功能在C#和D中是已經(jīng)實(shí)現(xiàn)的。由于數(shù)組在C#中是不可變的,從tokens中返回一個(gè)沒(méi)有空格的數(shù)組將導(dǎo)致重新分配一個(gè)數(shù)組。可是,D允許指派數(shù)組的長(zhǎng)度,這個(gè)能動(dòng)態(tài)地調(diào)整數(shù)組的大小這是個(gè)很好的例子說(shuō)明D語(yǔ)言提供更好的效率。

    總的來(lái)說(shuō),考慮到性能和自由度,我們能夠說(shuō) C++是勝利者,D是很接近的第二名。C擁有最佳的性能,但是只支持一種類型的tokerization。C#表現(xiàn)得很差,java更甚。

    總結(jié)

    我們可以把結(jié)果分成3種情況:這些包括語(yǔ)言的特性,包括庫(kù)的特性,或者兩者都相關(guān)。這些只與語(yǔ)言相關(guān)的特性顯示在 f2i, i2f, picalc, picalcr, and sieve scenarios,這些在語(yǔ)言選擇上的作用是很小的。c#看起來(lái)在總體上是最好的,但是有點(diǎn)讓人不能信服的是它在f2i中取得的優(yōu)異性能并且是因?yàn)闋奚烁↑c(diǎn)精確度而取得效率。(這一點(diǎn)需要更多大檢查,我將在下一篇文章中去做。)

    java是明顯最差的

    當(dāng)討論intel編譯器的浮點(diǎn)計(jì)算的深度優(yōu)化效果時(shí),它很明顯地顯示,c和c++能夠做的比C#更好。我討論這些并不是說(shuō)這些語(yǔ)言必須要更好的效率,而是說(shuō)他們能比其他的語(yǔ)言提供更好的優(yōu)化和增強(qiáng)的機(jī)會(huì)。這個(gè)大概是與本主題完全不相干的是,C和C++比別的語(yǔ)言有更多的編譯器;更多的競(jìng)爭(zhēng),更聰明的人擁有不同的策略和技術(shù)。編譯器的結(jié)果的運(yùn)行目標(biāo)是處理器,這將導(dǎo)致戲劇性的效果;我們僅僅在表面上探討了這個(gè)龐大的問(wèn)題,但是我不久將在以后的文章中回到這個(gè)主題。

    Strtok 是唯一一個(gè)能說(shuō)是library-only的,在這一點(diǎn)上C#干的并不好。雖然比java快,但是比其他的語(yǔ)言慢2倍或更多。同樣令人失望的是VisualC/C++的運(yùn)行庫(kù)。在這一點(diǎn)上包括語(yǔ)言和庫(kù)的效果的是i2str,str2i,strcat和strtswtch,描繪的并不是很清楚。C#明顯的在string的拼接這一環(huán)節(jié)上比java好得多,但是明顯地比其他的差。關(guān)于尊敬的C,C++,D,只是在一些環(huán)節(jié)上領(lǐng)先,在另外一些環(huán)節(jié)上相當(dāng)?shù)夭睢?

    很有趣的是定制的C++的庫(kù)替換加上深度優(yōu)化的intel的編譯器。

    總之,不可能做出定量的結(jié)論。從語(yǔ)言的表層來(lái)看, C# 和 Java 挺簡(jiǎn)單的,但低效的庫(kù)降低了速度。我覺(jué)得,當(dāng)相關(guān)的庫(kù)還不如語(yǔ)言本身的時(shí)候, C# 的確實(shí)有微弱的優(yōu)勢(shì);但反過(guò)來(lái),當(dāng)庫(kù)逐漸完善,超過(guò)語(yǔ)言本身后, C# 明顯就不行了。但還是能看到一點(diǎn)點(diǎn)希望: C# 還是有可能實(shí)現(xiàn)預(yù)期的目標(biāo),因?yàn)閹?kù)比語(yǔ)言更容易實(shí)現(xiàn)組裝。但是,人們認(rèn)為由于跟別的語(yǔ)言相比, C# 和 Java 所用的庫(kù)與語(yǔ)言結(jié)合的更加緊密,這些庫(kù)就可以作為這兩種語(yǔ)言效率的關(guān)鍵所在,至少對(duì)當(dāng)前的版本來(lái)說(shuō)是這樣的。

    正如我所作出的示范(從整數(shù)到字符)以及相關(guān)提到的(從字符到整數(shù)),有人可能會(huì)批評(píng)者并不符合這篇文章的原意。

    更深入的探討

    本文主要探討了一下 C# 對(duì) C++ 和 Java 可能造成的“威脅”。總的來(lái)說(shuō),這結(jié)果雖然不怎么鼓舞人心,卻足以讓人吃驚。從效率上看, C# 跟 C 以及 C++ 這些過(guò)去的同類產(chǎn)品(假如它們是更高效的)相比只能算是一般水平,至少在基本語(yǔ)言特征的比較上是這樣的。從某種程度上來(lái)說(shuō), Java 也是如此。(我承認(rèn)我無(wú)法證明 C 和 C++ 相對(duì)于 C# 的絕對(duì)優(yōu)勢(shì)。在多年前我的畢業(yè)論文分析調(diào)查期間,我認(rèn)識(shí)到令人乏味的結(jié)果正和令人興奮的結(jié)果一樣生動(dòng)形象,但前者花費(fèi)更小的經(jīng)濟(jì)支出。)

    上述結(jié)論對(duì)于多處理器以及(高性能的文件/網(wǎng)絡(luò)處理系統(tǒng))等來(lái)說(shuō)卻不一定適用。不過(guò),這確實(shí)很好地展現(xiàn)出了這些語(yǔ)言的基本效率以及更復(fù)雜的運(yùn)算所依賴的最基本的庫(kù)。

    就 D 發(fā)展?fàn)顩r來(lái)看,現(xiàn)在就對(duì)它進(jìn)行這樣的歸類似乎為時(shí)過(guò)早,不過(guò)它現(xiàn)在的表現(xiàn)確實(shí)不錯(cuò)。 D 的效率至少是 C# 的 167% ,其實(shí)大多數(shù)情況下還不止。有些人指出,目前僅僅還處于字符時(shí)代,只需幾個(gè)人就能完成一個(gè)編譯器及其附帶的庫(kù)。我猜測(cè), D 有可能發(fā)展成一門(mén)強(qiáng)大的技術(shù)。

    就我個(gè)人而言,作為一個(gè)對(duì) C# 持有偏見(jiàn)的人,偶然還是會(huì)對(duì)它的性能感到驚訝。我熱切地期盼著能將性能的對(duì)比分析延伸到更多領(lǐng)域:復(fù)雜而高效的內(nèi)存的應(yīng)用,多線程,進(jìn)程通信,特殊文件處理,手寫(xiě)代碼開(kāi)銷(xiāo),這些技術(shù)都能讓我們對(duì)語(yǔ)言進(jìn)行更深入的研究。能看到 Inter 公司怎樣通過(guò)定制的 C Library (比如 CRunTiny ;參考 http://cruntiny.org/ )和 STL (比如 STLPort )。

    鳴謝

    感謝 Walter Bright 為我提供了一份言簡(jiǎn)意賅的關(guān)于 D 的介紹,并及時(shí)地指出了一些當(dāng)時(shí)被我忽略的優(yōu)點(diǎn),使我能對(duì)測(cè)試工程進(jìn)行改進(jìn)。同時(shí)也要感謝 Sun Microsystems 公司的 Gary Pennington ,他為我提供了關(guān)于 Java 方面的資料。還有 Scott Patterson ,他對(duì)本文的草稿進(jìn)行了大量的精簡(jiǎn),并對(duì)全篇文章進(jìn)行了細(xì)致的檢查。

    C# 性能能趕上編譯型的 C/C++/D 和中間代碼運(yùn)行時(shí)解釋的 java 嗎?

    微軟發(fā)布了 .net 平臺(tái)和 .net 平臺(tái)支持的多種語(yǔ)言,包括 C#,vb.net, 受管 (managed extensions) 的 C++. 它們構(gòu)成的體系能同時(shí)滿足開(kāi)發(fā)出高效的服務(wù)器架構(gòu),和開(kāi)發(fā)部門(mén)對(duì)強(qiáng)壯的多功能的接口的需求.

    和其他眾多程序員一樣,拿新開(kāi)發(fā)語(yǔ)言與日常用到的語(yǔ)言作比較和選擇的時(shí)候,我對(duì)新玩意總有莫名的偏見(jiàn)和懷疑.本篇文章將會(huì)討論 .net 平臺(tái)的優(yōu)點(diǎn),特別是相關(guān)的 C#. 我們拿 C# 和其他開(kāi)發(fā)語(yǔ)言在高性能軟件開(kāi)發(fā)方面,作定量的性能比較.

    因?yàn)?c# 程序編譯運(yùn)行分為兩部分:先編譯成中間代碼,然后是中間代碼的運(yùn)行時(shí)解釋執(zhí)行,所以我們不僅會(huì)把他跟純編譯型的 C/C++/D 語(yǔ)言相比,也會(huì)把他與典型的 ( 中間代碼運(yùn)行時(shí)解釋 ) 的 Java 語(yǔ)言作比較. Java 無(wú)疑是一個(gè)成熟的語(yǔ)言,應(yīng)用在各個(gè)計(jì)算機(jī)領(lǐng)域. Java 程序先被編譯成中間代碼,然后運(yùn)行時(shí)通過(guò) java 虛擬機(jī)解釋成本地機(jī)器碼執(zhí)行.由此看來(lái), .net 平臺(tái),特別是為首的 c# 語(yǔ)言,借鑒了 java 的很多東西,包括語(yǔ)法和運(yùn)行時(shí)支持.

    對(duì)新語(yǔ)言(或者說(shuō)時(shí)語(yǔ)言 + 對(duì)應(yīng)的庫(kù) + 該語(yǔ)言的運(yùn)行時(shí)支持)作完整公平的性能比較算是一個(gè)龐大考驗(yàn).本文對(duì) c# 與 C/C++/D 就開(kāi)發(fā)中都涉及到的基本共通點(diǎn)作初步的性能比較,同時(shí)還包括各個(gè)語(yǔ)言所帶的庫(kù)的性能比較.包括:

    1. 運(yùn)行一次完整的程序(轉(zhuǎn)載執(zhí)行一個(gè)程序的時(shí)間)

    2. 算法和遞歸(計(jì)算圓周率和 Eratorshnes's sieve )

    3. 類型轉(zhuǎn)換( int->float,float->int,int->string.string->int )

    4. 字符串比較

    5. 字符串連接

    6.( 字符串特定符號(hào)統(tǒng)計(jì) )

    通過(guò)上面的一系列比較,將會(huì)揭示 c# 編譯器效力的一些有意思的地方和 .net 運(yùn)行庫(kù)的性能

    背景 :

    雖然作比較的語(yǔ)言都可以算是 C 語(yǔ)言系列,雖然這五種語(yǔ)言在語(yǔ)法上只有或多或少的不同,但在其他方面,它們的差別是巨大的.(見(jiàn)表一, http://www.digitalmars.com/d/comparison.html ) . 我假設(shè)讀者已經(jīng)熟悉 C/C++/Java ,以及一些基本的 C# .對(duì)于 D 語(yǔ)言,不如其他語(yǔ)言那么有名,但你可以找到它的介紹

    D 語(yǔ)言介紹

    D 語(yǔ)言是 Walter Bright 正在開(kāi)發(fā)的一種語(yǔ)言,安裝和學(xué)習(xí)這種語(yǔ)言的開(kāi)銷(xiāo)很小。實(shí)質(zhì)上, D 語(yǔ)言的目標(biāo)是更成功的 C/C++ 語(yǔ)言,改進(jìn)包括可變長(zhǎng)度的數(shù)組,函數(shù)參數(shù)的 [in][out] 屬性,自動(dòng)內(nèi)聯(lián),版本控制,局部函數(shù),內(nèi)建單元測(cè)試,嚴(yán)格的類型檢查等等,包括 Java/C# 中實(shí)現(xiàn)的忽略指針和引用的差別 , 這在 C++ 中只有通過(guò)模板實(shí)現(xiàn)。它同時(shí)支持對(duì)現(xiàn)有 C 函數(shù)和 API 的直接支持。

    它支持許多跟 Java/C# 類似的特性 , 比如垃圾收集。 D 語(yǔ)言跟 C#/Java 的本質(zhì)區(qū)別是 D 語(yǔ)言運(yùn)行時(shí)不需要運(yùn)行時(shí)的虛擬機(jī)支持。 D 語(yǔ)言所支持的所有特性都是在編譯連接時(shí)完成的,所以不需要運(yùn)行時(shí)的額外支持。 D 語(yǔ)言的開(kāi)發(fā)目前已經(jīng)到了 Alpha 版本,將來(lái)的成熟版本在絕大多數(shù)情況下將會(huì)比 Java/C# 高效得多,除非 JIT 優(yōu)化非常明顯.所以我們才把 D 語(yǔ)言加到我們的測(cè)試當(dāng)中。實(shí)際上, Walter 告訴我們, D 完全有能力產(chǎn)生跟 C 代碼運(yùn)行得一樣快的代碼,同時(shí),由于自動(dòng)內(nèi)聯(lián),內(nèi)建數(shù)組,字符串內(nèi)建優(yōu)化,非 \0 的字符串結(jié)束標(biāo)記等特性,他相信在 D 語(yǔ)言正式發(fā)布的時(shí)候,它的性能將會(huì)超過(guò) C 。

    在真正的測(cè)試開(kāi)始以前,我假定性能按照從高到低排序,應(yīng)該是 C/C++,D,C#,Java .我跟其隨后證實(shí)的其他好多人一樣,一開(kāi)始就懷疑 C# 產(chǎn)生高性能代碼的能力.

    性能比較

    我們會(huì)在下面接著介紹的十一種不同情況下測(cè)試性能.測(cè)試平臺(tái)是 2G 512M 的 pentium4 ,操作系統(tǒng)是 Windows XP Pro. 測(cè)試是在一套我自己設(shè)計(jì)的工具下進(jìn)行的,為了符合這篇文章,我理所當(dāng)然地用 C# 寫(xiě)了這個(gè)工具。在沒(méi)有其他干擾進(jìn)程的情況下,對(duì)每個(gè)測(cè)試者運(yùn)行七次,分別去掉最快的一次和最慢的一次后得到的品均值,就是測(cè)試者的成績(jī)。除了下面說(shuō)到的 noop 測(cè)試,所有的測(cè)試通過(guò)高精確度定時(shí)器計(jì)算完成所有內(nèi)循環(huán)所需時(shí)間完成。(譯者注:比如 int->float 測(cè)量中,會(huì)通過(guò)循環(huán)運(yùn)行多個(gè) int->float 的轉(zhuǎn)換,然后測(cè)量完成整個(gè)循環(huán)需要的時(shí)間)。如 Listing1 所示的代碼,就是用 c# 完成的 f2i 測(cè)試。 (C 通過(guò)調(diào)用 win32 api 函數(shù) QueryPerformanceCounter() ; C++ 通過(guò) WinSTL 的 performance_counter;C# 通過(guò) SynSoft.Performance.PerformanceCounter;D 通過(guò) syncsoft.win32.perf.PerfomanceCounte ; Java 通過(guò) System.currentTimeMillis()) 。每個(gè)程序都包含進(jìn)入實(shí)質(zhì)測(cè)試代碼前一段 ” 熱身代碼 ” 來(lái)減少程序初始化 ,catch 等因素帶來(lái)的不利影響。

    編譯器版本號(hào)如下:

    C#:VC#.net v7.00.9466,.NET Framework v1.0.3705;

    D: Digital Mars D compiler Alpha v0.61

    Java: J2KDSE 1.4.1

    C/C++ 在兩個(gè)編譯器下完成,分別是 Digital Mars C/C++ v8.33,STL Port v4.5 和 Inter C/C++ v7.0 ,頭文件和庫(kù)用 VC++ 6.0

    用 Digital Mars 編譯器的原因是:首先它能快速生成高質(zhì)量代碼,而且免費(fèi)。其次是為了和 D 語(yǔ)言作比較,因?yàn)?D 語(yǔ)言用到相同的 C/C++ 連接器和很多 C 庫(kù)。

    為了作徹底的性能比較,所有的編譯器都用了速度最優(yōu)的優(yōu)化方案。 Inter 編譯選項(xiàng)是 -QaxW ,它提供兩種代碼路徑,一種是專門(mén)為 Pentium4+SIMD 優(yōu)化,一種是為其他 cpu 優(yōu)化。具體的代碼運(yùn)行路徑在運(yùn)行時(shí)自動(dòng)選擇。 ( 通過(guò)對(duì)不同 C/C++ 編譯器性能的分析,我認(rèn)為 Inter 編譯器針對(duì)他自己芯片的優(yōu)化非常好,而對(duì)于 Digital Mars 的編譯器編譯出來(lái)的代碼性能,可以代表普遍的 C/C++ 性能 )

    對(duì)這幾種語(yǔ)言作方方面面的比較是不太現(xiàn)實(shí)的,因?yàn)槟承┨囟ǖ墓δ懿](méi)有在這幾種語(yǔ)言中都得到實(shí)現(xiàn)。比如 C 內(nèi)建庫(kù)里沒(méi)有(字符串特定符號(hào)統(tǒng)計(jì))函數(shù)。不過(guò)既然這篇文章的主題是討論 C#, 所做的測(cè)試都是 C# 支持的,如果其他語(yǔ)言沒(méi)有提供對(duì)應(yīng)的功能,我們會(huì)提供一個(gè)自定義的實(shí)現(xiàn)。同時(shí),如果某種語(yǔ)言對(duì)某種功能的內(nèi)建支持非常差的話,我會(huì)考慮提供一個(gè)相對(duì)好一點(diǎn)的自定義支持。 ( 特別是對(duì)于 C/C++ ,至少有一兩個(gè)地方看得出效率不高的內(nèi)建庫(kù)掩蓋了語(yǔ)言本身的真實(shí)性能 )

    簡(jiǎn)而言之,我不會(huì)列出每種測(cè)試在不同語(yǔ)言的具體實(shí)現(xiàn)代碼,不過(guò)在這里,我會(huì)詳細(xì)介紹下面幾種測(cè)試情況 :

    noop.noop 測(cè)試的是程序的裝載時(shí)間 : 一個(gè)空的 main/Main 的執(zhí)行時(shí)間。跟其它測(cè)試不一樣,這個(gè)測(cè)試是在 ptime 工具下完成的 .( 可以從 http://synesis.com.au/r_systools.html)  獲得,它提供了了 unix 環(huán)境下 time 工具類似的功能,同時(shí)它還能多次運(yùn)行目標(biāo)程序來(lái)獲得平均值 ( 在我們的測(cè)試中, C/C++/D 運(yùn)行 200 次 ,C# 運(yùn)行 50 次, java 運(yùn)行 30 次 ). 為了除去某些特定情況造成的影響,我們會(huì)多次測(cè)量 ( 譯者注:指多次運(yùn)行 ptime 程序, ptime 程序自身又會(huì)多次運(yùn)行目標(biāo)程序 ) ,并且取去掉最高值和最低值后的平均值。

    f2i 這個(gè)測(cè)試把 1 個(gè)長(zhǎng)度為 10,000 的雙精度浮點(diǎn)值轉(zhuǎn)換為 32bit 的整數(shù)。從 Listing 1 可以看出,使用偽隨機(jī)數(shù)產(chǎn)生算法可以保證各語(yǔ)言比較相同的浮點(diǎn)數(shù)。這帶來(lái)了兩個(gè)有趣的情況,我們將在查看結(jié)果的時(shí)候予以討論。

    I2f 跟前一個(gè)相反,這個(gè)測(cè)試對(duì)針對(duì)具體某個(gè)函數(shù)進(jìn)行的測(cè)試。程序生成 10000 個(gè)確定的偽隨機(jī)整數(shù),保證在不同語(yǔ)言中被轉(zhuǎn)換的整數(shù)是相同的。

    I2str 把整型轉(zhuǎn)換成成字符串型。整型變量從 0 循環(huán)增加到 5000000 ,每次做一個(gè)整型到字符串型的轉(zhuǎn)換 ( 見(jiàn) listing2).C#/Java 用內(nèi)建函數(shù)完成 ---i.ToString() 和 Integer.Tostring(i),C/D 用傳統(tǒng)的 sprintf() 完成。 C++ 分為兩次完成。第一次用 iostream 的 strstream 類,和預(yù)期的一樣,這種情況下的性能非常差。為了提高效率,我采取了一些措施,所以導(dǎo)致了第二種方案,見(jiàn) i2str2

    Str2i. 把字符串轉(zhuǎn)換成整型。建立一個(gè)字符串?dāng)?shù)組,內(nèi)容是 i2str 轉(zhuǎn)換過(guò)來(lái)的內(nèi)容,然后再通過(guò)語(yǔ)言本身的支持或者庫(kù)函數(shù)把字符串轉(zhuǎn)換回去,計(jì)算轉(zhuǎn)換回去的時(shí)間(見(jiàn) listing3 ) .C/D 用 atoi 完成, c++ 用 iostream 的 strstream 完成, c# 用 int32.parse 完成, java 用 integer.parseint() 完成。

    picalc 迭代和浮點(diǎn)。通過(guò) listing 4 所示的迭代算法計(jì)算圓周率。每種語(yǔ)言的實(shí)現(xiàn)其迭代次數(shù)是 10,000,000 。



    Picalcr. 遞歸。通過(guò) listing5 所示的算法計(jì)算圓周率。對(duì)每種語(yǔ)言,我們會(huì)做 10000 次運(yùn)算,考慮到 java 堆棧在深層地歸時(shí)很容易被耗盡,所以每次運(yùn)算限制在 4500 次遞歸以內(nèi)。


    Sieve. 疊代。通過(guò) Listing 6 所示的迭代算法( Eratosthenes's sieve ( http://www.math.utah.edu/~alfeld/Eratorthenes.html ))得到質(zhì)數(shù)。每種語(yǔ)言的實(shí)現(xiàn)其迭代次數(shù)是 10,000,000 。


    Strcat. 字符串連接 --- 通常是大量運(yùn)行時(shí)開(kāi)銷(xiāo)的根源,也是導(dǎo)致程序低效的潛在領(lǐng)域。(我以前做過(guò)一個(gè)對(duì)性能敏感的項(xiàng)目,通過(guò)改善效率底下的字符串連接,我獲得了 10% 的整體性能提升) . 測(cè)試通過(guò)構(gòu)造四種不同類型的變量 :short,int, double 和一個(gè) string ,然后把它們連接成一個(gè) string 變量完成,連接時(shí)添加用分號(hào)分割.在連接的時(shí)候采用四種不同的連接方

    法 :1) 默認(rèn)連接方法 2) 故意構(gòu)造出來(lái)的導(dǎo)致效率低下的連接方法 3)C 語(yǔ)言種典型的 printf/Format 格式,需要重新分配空間 4) 為提高性能而進(jìn)行的硬編碼 . 代碼摘錄見(jiàn) Listing7



    Strswtch. 通過(guò)級(jí)聯(lián) switch 進(jìn)行字符串比較 . 通過(guò)語(yǔ)言支持的 switch 語(yǔ)句進(jìn)行比較或者通過(guò)一些列的 if-else 語(yǔ)句模擬( Listing 8 ).這項(xiàng)測(cè)試是為了檢驗(yàn) c# 的字符串處理功能是否如他所聲稱的那樣高效.測(cè)試分成兩部分 : 首先是進(jìn)行字符串之間的賦值,也就是同一身份的比較(譯者注:也就是字符串內(nèi)部的指針直接復(fù)制,不涉及到字符串內(nèi)容的拷貝).其次是通過(guò)字符串的拷貝后進(jìn)行比較,這樣就避免了同一身份比較而是進(jìn)行進(jìn)行字符串之間的字面比較.每種語(yǔ)言上都進(jìn)行 850000 次疊代,因?yàn)槌^(guò)這個(gè)次數(shù)后會(huì)導(dǎo)致 java vm 內(nèi)存耗盡.

    strtok 字符串分段。字符串的運(yùn)行效率也極低,有時(shí)甚至引發(fā)嚴(yán)重的運(yùn)行開(kāi)銷(xiāo)。這個(gè)測(cè)試( Listing 9)讀取一個(gè)文本文件的全部?jī)?nèi)容至一個(gè)字符串,然后拆分成段。第一個(gè)變量用字符分隔符:‘;'分隔字符串,并忽略所有的空白。第二個(gè)變量也用分號(hào)分隔,但保留了空白。第三個(gè)和第四個(gè)變量用字符串分隔符:<->,并相應(yīng)的忽略和保留了空白處。C里僅第二個(gè)變量用了strtok(),C++四個(gè)變量都用STLSoft的string_tokeniser模板,但各自正確給參數(shù)賦值。C#第一個(gè)變量用System.String.Split(),2至4變量用SynSoft.Text.Tokeniser.Tokenise();Java用java.util.StringTokenizer,僅支持變量2和4(注:我選用STLSoft的string_tokeniser模板不是我要為STLSoft作宣傳,而是測(cè)試表明它比Boost的快2.5倍,而B(niǎo)oost是大多數(shù)開(kāi)發(fā)人員在查找?guī)鞎r(shí)的首選)。


    結(jié)果

    在 noop測(cè)試中,表2列出了以微秒(us)為單位裝載一個(gè)空操作的平均開(kāi)銷(xiāo)。顯然C,C++,D的差別不合邏輯,每一個(gè)大約是5-6毫秒,而Intel確低至2ms。我所想到的唯一可能是這個(gè)編譯器優(yōu)化了所有的C實(shí)時(shí)運(yùn)行庫(kù)的初始化工作。如果是這樣,這個(gè)性能在其他非試驗(yàn)性即實(shí)際應(yīng)用中不能體現(xiàn)出來(lái),因?yàn)樗辽傩枰恍〤 … ,這有待深入研究。


    C#用了大約70ms,Java約0.2秒。C#和Java的裝載清晰的表明其實(shí)時(shí)開(kāi)銷(xiāo)在于它們的實(shí)時(shí)運(yùn)行的結(jié)構(gòu)基礎(chǔ)(VM和支持DLLs)。但是除了大規(guī)模的命令行的商業(yè)化批處理,對(duì)極大型系統(tǒng)(上百萬(wàn)條代碼)或CGI的基礎(chǔ)構(gòu)建,啟動(dòng)開(kāi)銷(xiāo)在大多數(shù)情況下不重要。重荷服務(wù)器允許在數(shù)秒內(nèi)啟動(dòng),所以語(yǔ)言間200ms的差異就微乎其微了。


    其余的結(jié)果分三部分顯示。圖 2和3分別展示了字符串連接和分段的結(jié)果。圖1包含了其余全部的測(cè)試結(jié)果,我們現(xiàn)在先來(lái)看看它。



    圖 1中,對(duì)各測(cè)試項(xiàng),C,C++,D,Java的結(jié)果均以其相應(yīng)測(cè)試項(xiàng)C#運(yùn)行時(shí)間的百分比顯示。因此,在100%線上下波動(dòng)的結(jié)果說(shuō)明與C#性能相當(dāng)。高百分比說(shuō)明C#性能較優(yōu),而低百分比表明C#性能相對(duì)較差。

    f2i 拋開(kāi)Intel不談,可以看出C#在浮點(diǎn)到整型的轉(zhuǎn)換中效率遠(yuǎn)遠(yuǎn)高于其余四個(gè),僅是C,C++, 和D開(kāi)銷(xiāo)的2/3,是Java的2/5。在我讓它們使用相同的隨機(jī)數(shù)發(fā)生器前,不同語(yǔ)言的性能差異極大,這說(shuō)明轉(zhuǎn)換速度非常依賴具體的浮點(diǎn)數(shù)值,當(dāng)然這是后見(jiàn)之明了。為了確保每個(gè)語(yǔ)言做相同的轉(zhuǎn)換,程序計(jì)算轉(zhuǎn)換值的總和,這也有助于任何過(guò)度的優(yōu)化去除整個(gè)循環(huán)。可以得知這種轉(zhuǎn)化在語(yǔ)言間正確度不同。每種語(yǔ)言的計(jì)算總和迭代10,000次的結(jié)果在表3中列處。它們表明(雖然無(wú)法證實(shí))C,C++,和D是最正確的,Java其次,C#最差。C#犧牲正確性和降低處理器浮點(diǎn)精度(參見(jiàn) 引用)換取優(yōu)越性能是令人信服的,但事實(shí)上做此判斷前我還需要更多的證據(jù)。至于Intel,說(shuō)它的性能沒(méi)有什么更卓越的也是公平的。


    5個(gè)語(yǔ)言這里效率相當(dāng)。另4種性能比C#低5%以內(nèi)。唯一叫人感興趣的是這四個(gè)語(yǔ)言都是性能比C#略好。這表明C#確實(shí)效率低,雖然在這方面只低一點(diǎn)。這與從整型到浮點(diǎn)型轉(zhuǎn)換的結(jié)果相反。同樣,值得我們注意的是,雖然Java在浮點(diǎn)到整型的轉(zhuǎn)換中性能很低,但在整型到浮點(diǎn)的轉(zhuǎn)換中卻是最好的,比C#高10%。對(duì)f2i,Intel表現(xiàn)極佳,其C和C++分別只用了C#時(shí)間的10%和23%。

    i2str 整型到字符型的轉(zhuǎn)換。 C#比C和D的printf略好。比C++的以iostream為基礎(chǔ)的轉(zhuǎn)換效率要高。它是C#運(yùn)行時(shí)間的4倍(對(duì)Intel 和VC6 STL/CRT是5倍)。但Java性能出色,用了C#2/5的時(shí)間。因?yàn)镃++的效率低下,第二個(gè)變量使用STLSoft的 integer_to_string<> 模板函數(shù)。它在很多編譯器上比C和C++庫(kù)的所有可利用的轉(zhuǎn)換機(jī)制要快。在這種情況下,性能比C#高10%,比C和D高約20%。但是因?yàn)镮ntel編譯器的優(yōu)化影響, integer_to_string<> 似乎找到了其完美搭檔:其時(shí)間比C#低30%,是第三個(gè)速度超過(guò)Java的,比Iostream快17倍以上。認(rèn)為它很可能已經(jīng)接近了轉(zhuǎn)換效率的上限也是合理的。

    str2i 字符型到整型的轉(zhuǎn)化結(jié)果與前面大相徑庭。 C和D用atoi比C#快約5-8倍,比Java快3倍,Java比C#效率高得多。但是這四個(gè)比C++以iostream為基礎(chǔ)的轉(zhuǎn)換都快。C++用 Digital Mars/STLPor運(yùn)行時(shí)間t 是 C#的2.5倍,用 Intel和VC6 CRT/STL 是 10倍。這是iostream的另一個(gè)致命弱點(diǎn)。但因此說(shuō)C++效率低下未免有失公允(實(shí)際上,我正在為 STLSoft 庫(kù)寫(xiě)字符至整型的轉(zhuǎn)換,以和它 integer_to_string<> 的杰出轉(zhuǎn)換性能相匹配。最初的結(jié)果表明它將優(yōu)于 C的atoi,所以這個(gè)轉(zhuǎn)換中C++也是優(yōu)于C的。我希望能在本文刊發(fā)前完成此轉(zhuǎn)換。請(qǐng)參閱 http://stlsoft.org/conversion_library.html 。 )

    picalc 在這里各語(yǔ)言性能表現(xiàn)相近。 C#和Java幾乎相同,比C,C++效率高上10%。C#的性能可以這樣解釋:它的浮點(diǎn)數(shù)操作效率高,這可以從f2I看出。但這不能理解Java的表現(xiàn)。我們可以認(rèn)為C#和Java都能較好的優(yōu)化循環(huán),它涉及函數(shù)調(diào)用,但這有賴于深入研究。既然性能相差10%以內(nèi),我們只能得出這樣的結(jié)論:各語(yǔ)言在循環(huán)結(jié)構(gòu)性能方面差異不顯著。有趣的是,Intel的優(yōu)化這里沒(méi)有實(shí)際效果,可能是因?yàn)镻i值計(jì)算太簡(jiǎn)單了吧。

    Picalcr : 這里的結(jié)果可以更加確定一點(diǎn), C , C++ , C# 和 D 語(yǔ)言的性能偏差在 2% 之內(nèi),我們可以公平地說(shuō)它們對(duì)于遞歸執(zhí)行的性能是一樣的。 Java 卻是花費(fèi)比其他四種語(yǔ)言多 20% 的時(shí)間,加上 JVM ( Java 虛擬機(jī))在超過(guò) 4500 次遞歸時(shí)堆棧耗竭,可以明顯地從速度和內(nèi)存消耗方面得出 Java 處理遞歸不是很好。 Intel 的優(yōu)化能力在這里有明顯的作用(讓人印象深刻的 10% ),但我在這里并沒(méi)有詳細(xì)分析這一點(diǎn)。

    Sieve: 相對(duì)簡(jiǎn)單的計(jì)算質(zhì)數(shù)的算法(僅包含迭代和數(shù)組訪問(wèn))說(shuō)明 C , C++ , C# 和 D 語(yǔ)言在這方面事實(shí)上是一樣的(僅 0.5% 的偏差),但 Java 的性能低了 6% 。我認(rèn)為這中間大有文章, C , C++ 代碼不進(jìn)行數(shù)組邊界安全檢查,而 C# 和 Java 進(jìn)行數(shù)組邊界檢查。我對(duì)這種結(jié)果的解釋是: C# 和 D 語(yǔ)言在 FOR 循環(huán)內(nèi)能根據(jù)對(duì)數(shù)組邊界的條件測(cè)試對(duì)邊界檢查進(jìn)行優(yōu)化,而 Java 不行。由于 C# 是新興語(yǔ)言, D 語(yǔ)言還沒(méi)發(fā)行,這就讓人不那么失望了。即使這不是為 Java 辯解,也不會(huì)讓人印象深刻了。再說(shuō), Intel 優(yōu)化起了明顯的作用——增長(zhǎng) 5% 性能,正如 picalcr 例子,這相對(duì)于其他語(yǔ)言更讓人印象深刻。

    Strswtch : 這個(gè)測(cè)試對(duì)于 C# 語(yǔ)言不是很好。即使是 C++ 低效的字符串類操作符 = = 都比 C# 的性能快 2.5 倍 (Digital Mars) 到 5 倍( Intel VC6 STL/CRT )。 .NET 環(huán)境下字符串是內(nèi)部處理的,這表示字符串存儲(chǔ)在全局離散表中,副本可以從內(nèi)存中消除,等值測(cè)試是以一致性檢查的方式進(jìn)行的 ( 只要兩個(gè)參數(shù)都是在內(nèi)部 ) 。這明顯表示內(nèi)部修復(fù)機(jī)制的效率嚴(yán)重低下或者一致性檢查沒(méi)有建立 C# 的 ”string-swatching” 機(jī)制性。 C# 的這種嚴(yán)重低效率更讓人傾向于后者,因?yàn)橄氩坏絺€(gè)好的理由解釋為什么會(huì)這樣。正如例 8 所示,變量 1 的字符串實(shí)例是文字的,都在內(nèi)部(事實(shí)上,這里有個(gè)限制沒(méi)有在例 8 中表示出來(lái),在變量 1 的預(yù)循環(huán)中用來(lái)驗(yàn)證參數(shù)是真正在內(nèi)部的)。

    當(dāng)在變量 2 中假裝進(jìn)行一致性檢查,發(fā)現(xiàn)僅 Java 的性能有明顯的提高,從 2% 到令人印象深刻的 30% 。很明顯 Java 在使用一致性檢查所取的性能是不切實(shí)際的,因?yàn)樵谡嬲某绦蛑胁荒芟拗谱址际俏淖值摹5@可用來(lái)說(shuō)明支持內(nèi)部處理和提供可編程實(shí)現(xiàn)訪問(wèn)這種機(jī)制的語(yǔ)言的一種性能。諷刺的是, C# 作為這五種語(yǔ)言之一,它的性能是最差的。

    Strcat : 表 2 以微秒( μs )顯示了五種語(yǔ)言每一種在這個(gè)例子中的四個(gè)變量的花費(fèi)時(shí)間。本文沒(méi)有足夠的篇幅來(lái)細(xì)節(jié)描述 17 執(zhí)行代碼,但盡量保持例子 7 的四個(gè)變量的真實(shí)面貌。變量 1 和 2 在所有語(yǔ)言執(zhí)行代碼中涉及以堆棧為基礎(chǔ)的字符串內(nèi)存,同時(shí) C , D 語(yǔ)言在第三個(gè)變量中、 C , C++ , D 語(yǔ)言在第四個(gè)變量中用結(jié)構(gòu)內(nèi)存,這在某種程度說(shuō)明他們的性能優(yōu)于其他變量和語(yǔ)言的執(zhí)行代碼。本例子的 C , C++ 的討論適合于 Digital Mars 版本,因?yàn)楹苊黠@的 Intel 所帶來(lái)的性能提高被 Visual C++ 運(yùn)行時(shí)刻庫(kù)的效率(非期望)所抵消,事實(shí)上 Intel 的每種測(cè)試的性能都比 Digital Mars 的差。

    第一種測(cè)試是以默認(rèn)的方式執(zhí)行的,很顯然 C# 和 D 語(yǔ)言的性能都好于其它語(yǔ)言。很奇怪的是 Java 的性能最差,因?yàn)?Java 被認(rèn)為有能力在一條語(yǔ)句中把字符串序列解釋成字符串構(gòu)件格式(在變量 4 中手工轉(zhuǎn)換)。

    第二種測(cè)試,眾所周知它的格式不好,透露了很多東西。它的劣性根據(jù)語(yǔ)言而不同,對(duì)于 C# , D 和 Java ,包含在連續(xù)的表達(dá)式中連接每一項(xiàng),而不是象第一個(gè)變量在一個(gè)表達(dá)式中。對(duì)于 C ,它簡(jiǎn)單地省略了在開(kāi)始多項(xiàng)連接重新分配之前的的內(nèi)存請(qǐng)求。對(duì)于 C++ ,它使用 strstream 和插入操作符而不是使用 str::string 和 + ()操作符。結(jié)果顯示所有語(yǔ)言不同程度地降低性能。 C 內(nèi)存分配器的附加輪詢看起來(lái)沒(méi)有起多大作用,可能因?yàn)闆](méi)有競(jìng)爭(zhēng)線程造成內(nèi)存碎片,所以消耗同樣的內(nèi)存塊。 C++ 改變成 iostreams 使代碼更緊湊,但沒(méi)有轉(zhuǎn)變數(shù)字部分,它們僅被插入到流當(dāng)中,所預(yù)計(jì)的執(zhí)行結(jié)果驗(yàn)證了這一點(diǎn)。 D 因?yàn)檫@個(gè)改變降低性能許多( 32% ), Java 更多( 60% )。很奇怪的是 C# 性能在這里僅降低很小,少于 7% 。這讓人印象非常深刻,意味著可能在 .NET 的開(kāi)發(fā)環(huán)境下因?yàn)?Java 字符串連接而形成的代碼回查的警覺(jué)本能會(huì)衰退。

    第三種測(cè)試使用 printf()/Format() 的變量不足為奇。通過(guò)使用部分 / 全部的幀內(nèi)存, C , C++ 和 D 達(dá)到很好的性能。奇怪的是 C# 的性能僅是 22% ,簡(jiǎn)直不值一提。 Java 根本沒(méi)有這個(gè)便利功能。(以個(gè)人的觀點(diǎn)也是不值一提的) C#和D語(yǔ)言都能夠在封閉的循環(huán)中,依照數(shù)組邊界測(cè)試情況,優(yōu)化他們的越界檢查。

    最大的 測(cè)試 -硬編碼的性能-這個(gè)是很有趣的問(wèn)題,因?yàn)?C++還是能提供超級(jí)的性能,如果能夠依照問(wèn)題的精確領(lǐng)域來(lái)編碼,即使別的語(yǔ)言(C#,java)使用默認(rèn)的或者性能優(yōu)化的組件或者理念也是如此。在C#和java上使用他們各自的StringBuilders能夠提供真實(shí)的效果,達(dá)到他們?cè)谶@個(gè)集合中的最佳性能。然而,C#能發(fā)揮最佳效率的機(jī)制,還是不能與C/C++、D語(yǔ)言相比。更糟糕的是,java的最佳性能都比別的語(yǔ)言的最差性能要可憐,這是相當(dāng)可悲的。

    總之,我們說(shuō)對(duì)底層直接操作的語(yǔ)言垂手可得地贏得那些轉(zhuǎn)化率工作得很好的或者完全地在中間代碼中,并且很顯然地能夠提供良好性能的語(yǔ)言用法對(duì)java是很重要的而對(duì)C#是不太重要的。請(qǐng)注意這一點(diǎn)。

    Strtok . Table3顯示了五種語(yǔ)言的每一個(gè)環(huán)節(jié)總共的時(shí)間(單位是毫秒ms)。同時(shí)他們的空間占用在這篇文章中并沒(méi)有提及,但是所有語(yǔ)言的實(shí)現(xiàn)都是非常直接的并且很明顯很接近在Listing9中列出的四個(gè)問(wèn)題的本質(zhì)。與strcat一樣,基于同樣的原因,使用Intel編譯器的結(jié)果沒(méi)有討論。

    第一個(gè)問(wèn)題――分割字符,保留空格――顯示C++版本是明顯的勝利者,D語(yǔ)言要慢20%,C# 要3倍的時(shí)間。顯然,這個(gè)與庫(kù)的特征的關(guān)系比語(yǔ)言的更明顯,

    并且我使用 tokenizer比著名的Boost更多一些,那樣的話,D語(yǔ)言要比C++快2倍,并且C#只慢20%。雖然如此,STLSoft的tokenizer是免費(fèi)得到,我想我們應(yīng)該把這個(gè)作為C++的一個(gè)勝利。(其實(shí),template在這一點(diǎn)上的使用STL的basic_string作為他的值類型,這個(gè)并不因?yàn)樗牧己眯阅芏劽@可能引起爭(zhēng)論-我并沒(méi)有使用一個(gè)更有效率的string,就像STLSoft的frame_string.總之,我認(rèn)為這是個(gè)公平比較)。

    第二個(gè)問(wèn)題--分割字符,去掉空格--包含所有語(yǔ)言的實(shí)現(xiàn)版本。自然地,C語(yǔ)言贏得了比賽,它使用了strtok()函數(shù),這個(gè)函數(shù)在創(chuàng)建tokens時(shí)并不分配內(nèi)存并且直接寫(xiě)終結(jié)符NULL到tokenized string的結(jié)尾。盡管有這些不利條件,C++的性能還是很好的,時(shí)間是C的221%,比較好的是D語(yǔ)言,432%。

    C#和java就很差了,分別是746%和957%。這簡(jiǎn)直是不敢相信的,C#和java運(yùn)行的時(shí)間是C++的3.4倍和4.3倍,這三種語(yǔ)言都為tokens分配了內(nèi)存。我相信這個(gè)是STL模型在處理iterable sequences時(shí)比passing around arrays更有效率的很好的例子。(請(qǐng)注意,我們有高度的信心這是場(chǎng)公平的關(guān)于C++,C#和D語(yǔ)言的比較,我寫(xiě)了三種tokerizer的實(shí)現(xiàn),都是公開(kāi)的,可得到的.)

    第三個(gè)問(wèn)題--分割句子,保留空格--顯示了兩件事情。第一,在這三種語(yǔ)言在實(shí)現(xiàn)這個(gè)程序的代價(jià)比實(shí)現(xiàn)單個(gè)字符更昂貴。第二,我們發(fā)現(xiàn)D語(yǔ)言取代了C++的最佳性能的地位,領(lǐng)先5%左右。C#繼續(xù)被甩下,大概是另外兩種語(yǔ)言的1.8倍時(shí)間左右。

    第四個(gè)問(wèn)題--分割句子,去掉空格--沒(méi)有任何令人意外的,C++和D擁有大概一致的性能。C#比較不錯(cuò)(大概2倍的性能差距)java更慢(差不多3倍時(shí)間)。對(duì)于C++,C#和D來(lái)說(shuō)去掉空格比不去掉空格導(dǎo)致一小點(diǎn)的性能損失。很明顯的,在C#中,這么做比其他兩個(gè)的損失更大一點(diǎn)。很有價(jià)值的是,這些功能在C#和D中是已經(jīng)實(shí)現(xiàn)的。由于數(shù)組在C#中是不可變的,從tokens中返回一個(gè)沒(méi)有空格的數(shù)組將導(dǎo)致重新分配一個(gè)數(shù)組。可是,D允許指派數(shù)組的長(zhǎng)度,這個(gè)能動(dòng)態(tài)地調(diào)整數(shù)組的大小這是個(gè)很好的例子說(shuō)明D語(yǔ)言提供更好的效率。

    總的來(lái)說(shuō),考慮到性能和自由度,我們能夠說(shuō) C++是勝利者,D是很接近的第二名。C擁有最佳的性能,但是只支持一種類型的tokerization。C#表現(xiàn)得很差,java更甚。

    總結(jié)

    我們可以把結(jié)果分成3種情況:這些包括語(yǔ)言的特性,包括庫(kù)的特性,或者兩者都相關(guān)。這些只與語(yǔ)言相關(guān)的特性顯示在 f2i, i2f, picalc, picalcr, and sieve scenarios,這些在語(yǔ)言選擇上的作用是很小的。c#看起來(lái)在總體上是最好的,但是有點(diǎn)讓人不能信服的是它在f2i中取得的優(yōu)異性能并且是因?yàn)闋奚烁↑c(diǎn)精確度而取得效率。(這一點(diǎn)需要更多大檢查,我將在下一篇文章中去做。)

    java是明顯最差的

    當(dāng)討論intel編譯器的浮點(diǎn)計(jì)算的深度優(yōu)化效果時(shí),它很明顯地顯示,c和c++能夠做的比C#更好。我討論這些并不是說(shuō)這些語(yǔ)言必須要更好的效率,而是說(shuō)他們能比其他的語(yǔ)言提供更好的優(yōu)化和增強(qiáng)的機(jī)會(huì)。這個(gè)大概是與本主題完全不相干的是,C和C++比別的語(yǔ)言有更多的編譯器;更多的競(jìng)爭(zhēng),更聰明的人擁有不同的策略和技術(shù)。編譯器的結(jié)果的運(yùn)行目標(biāo)是處理器,這將導(dǎo)致戲劇性的效果;我們僅僅在表面上探討了這個(gè)龐大的問(wèn)題,但是我不久將在以后的文章中回到這個(gè)主題。

    Strtok 是唯一一個(gè)能說(shuō)是library-only的,在這一點(diǎn)上C#干的并不好。雖然比java快,但是比其他的語(yǔ)言慢2倍或更多。同樣令人失望的是VisualC/C++的運(yùn)行庫(kù)。在這一點(diǎn)上包括語(yǔ)言和庫(kù)的效果的是i2str,str2i,strcat和strtswtch,描繪的并不是很清楚。C#明顯的在string的拼接這一環(huán)節(jié)上比java好得多,但是明顯地比其他的差。關(guān)于尊敬的C,C++,D,只是在一些環(huán)節(jié)上領(lǐng)先,在另外一些環(huán)節(jié)上相當(dāng)?shù)夭睢?

    很有趣的是定制的C++的庫(kù)替換加上深度優(yōu)化的intel的編譯器。

    總之,不可能做出定量的結(jié)論。從語(yǔ)言的表層來(lái)看, C# 和 Java 挺簡(jiǎn)單的,但低效的庫(kù)降低了速度。我覺(jué)得,當(dāng)相關(guān)的庫(kù)還不如語(yǔ)言本身的時(shí)候, C# 的確實(shí)有微弱的優(yōu)勢(shì);但反過(guò)來(lái),當(dāng)庫(kù)逐漸完善,超過(guò)語(yǔ)言本身后, C# 明顯就不行了。但還是能看到一點(diǎn)點(diǎn)希望: C# 還是有可能實(shí)現(xiàn)預(yù)期的目標(biāo),因?yàn)閹?kù)比語(yǔ)言更容易實(shí)現(xiàn)組裝。但是,人們認(rèn)為由于跟別的語(yǔ)言相比, C# 和 Java 所用的庫(kù)與語(yǔ)言結(jié)合的更加緊密,這些庫(kù)就可以作為這兩種語(yǔ)言效率的關(guān)鍵所在,至少對(duì)當(dāng)前的版本來(lái)說(shuō)是這樣的。

    正如我所作出的示范(從整數(shù)到字符)以及相關(guān)提到的(從字符到整數(shù)),有人可能會(huì)批評(píng)者并不符合這篇文章的原意。

    更深入的探討

    本文主要探討了一下 C# 對(duì) C++ 和 Java 可能造成的“威脅”。總的來(lái)說(shuō),這結(jié)果雖然不怎么鼓舞人心,卻足以讓人吃驚。從效率上看, C# 跟 C 以及 C++ 這些過(guò)去的同類產(chǎn)品(假如它們是更高效的)相比只能算是一般水平,至少在基本語(yǔ)言特征的比較上是這樣的。從某種程度上來(lái)說(shuō), Java 也是如此。(我承認(rèn)我無(wú)法證明 C 和 C++ 相對(duì)于 C# 的絕對(duì)優(yōu)勢(shì)。在多年前我的畢業(yè)論文分析調(diào)查期間,我認(rèn)識(shí)到令人乏味的結(jié)果正和令人興奮的結(jié)果一樣生動(dòng)形象,但前者花費(fèi)更小的經(jīng)濟(jì)支出。)

    上述結(jié)論對(duì)于多處理器以及(高性能的文件/網(wǎng)絡(luò)處理系統(tǒng))等來(lái)說(shuō)卻不一定適用。不過(guò),這確實(shí)很好地展現(xiàn)出了這些語(yǔ)言的基本效率以及更復(fù)雜的運(yùn)算所依賴的最基本的庫(kù)。

    就 D 發(fā)展?fàn)顩r來(lái)看,現(xiàn)在就對(duì)它進(jìn)行這樣的歸類似乎為時(shí)過(guò)早,不過(guò)它現(xiàn)在的表現(xiàn)確實(shí)不錯(cuò)。 D 的效率至少是 C# 的 167% ,其實(shí)大多數(shù)情況下還不止。有些人指出,目前僅僅還處于字符時(shí)代,只需幾個(gè)人就能完成一個(gè)編譯器及其附帶的庫(kù)。我猜測(cè), D 有可能發(fā)展成一門(mén)強(qiáng)大的技術(shù)。

    就我個(gè)人而言,作為一個(gè)對(duì) C# 持有偏見(jiàn)的人,偶然還是會(huì)對(duì)它的性能感到驚訝。我熱切地期盼著能將性能的對(duì)比分析延伸到更多領(lǐng)域:復(fù)雜而高效的內(nèi)存的應(yīng)用,多線程,進(jìn)程通信,特殊文件處理,手寫(xiě)代碼開(kāi)銷(xiāo),這些技術(shù)都能讓我們對(duì)語(yǔ)言進(jìn)行更深入的研究。能看到 Inter 公司怎樣通過(guò)定制的 C Library (比如 CRunTiny ;參考 http://cruntiny.org/ )和 STL (比如 STLPort )。

    鳴謝

    感謝 Walter Bright 為我提供了一份言簡(jiǎn)意賅的關(guān)于 D 的介紹,并及時(shí)地指出了一些當(dāng)時(shí)被我忽略的優(yōu)點(diǎn),使我能對(duì)測(cè)試工程進(jìn)行改進(jìn)。同時(shí)也要感謝 Sun Microsystems 公司的 Gary Pennington ,他為我提供了關(guān)于 Java 方面的資料。還有 Scott Patterson ,他對(duì)本文的草稿進(jìn)行了大量的精簡(jiǎn),并對(duì)全篇文章進(jìn)行了細(xì)致的檢查。

    ]]>
    posted on 2005-09-15 10:23 Sung 閱讀(6619) 評(píng)論(3)  編輯  收藏 所屬分類: software Development

    FeedBack:
    # re: C# 與 C 、 C++ 、 D 、 Java 的性能比較[第一部分]
    2011-10-23 22:35 | zmy
    c#1.0 不值得拿來(lái)說(shuō)
    4.0速度會(huì)讓你滿意的
    不知道5.0怎樣  回復(fù)  更多評(píng)論
      
    # re: C# 與 C 、 C++ 、 D 、 Java 的性能比較[第一部分]
    2011-10-23 22:40 | zmy
    另外c++的流的性能也是爛到了一定境界
    我用mmx優(yōu)化過(guò)的讀取文件內(nèi)整數(shù)字符串?dāng)?shù)組的性能是c++循環(huán)用流讀取的速度幾十倍  回復(fù)  更多評(píng)論
      
    主站蜘蛛池模板: 亚洲国产激情在线一区| 日韩一区二区三区免费体验| A片在线免费观看| 精品97国产免费人成视频| 久久99精品免费一区二区| 成人在线免费视频| 免费看黄福利app导航看一下黄色录像| 国产精品亚洲一区二区三区| jizzjizz亚洲日本少妇| 国产在亚洲线视频观看| 香蕉视频免费在线| 一个人看www免费高清字幕| 99视频免费在线观看| 无码人妻久久一区二区三区免费| 蜜桃成人无码区免费视频网站 | 亚洲中文字幕久久精品无码VA| 亚洲免费福利在线视频| 在线观看亚洲精品专区| 黄色免费网址大全| 国产一级婬片A视频免费观看| 国产免费爽爽视频在线观看| 99热这里只有精品免费播放| 手机看黄av免费网址| 天天摸夜夜摸成人免费视频 | 99热在线精品免费播放6| 在线a免费观看最新网站| 成人五级毛片免费播放| 免费中文字幕不卡视频| 伊人久久大香线蕉亚洲五月天 | 亚洲免费日韩无码系列| 亚洲AV永久无码精品| 亚洲国产美女精品久久久久| 亚洲国产综合AV在线观看| 一区二区免费电影| 91精品国产免费久久国语蜜臀| 欧洲黑大粗无码免费| 免费人成在线观看视频播放| 久久亚洲综合色一区二区三区 | 色久悠悠婷婷综合在线亚洲| 亚洲精品美女在线观看| 亚洲AV无码一区二区三区性色|