從三個語言(C++,Java,C#)的幾個性能測試案例來看性能優化


       隨着時間的發展,現在的虛擬機技術越來越成熟了,在有些情況下,Java,.Net等虛擬機密集計算的性能已經和C++相仿,在個別情況下,甚至還要更加優秀。本文詳細分析幾個性能測試案例,探討現象背后的原因。

       來看兩個簡單的測試用例。如下圖所示,均是循環5000次,操作 len = 1000000 的連續內存,計算執行時間。左側為test1,右側為test2。

       類似的程序在 .net core 3.0 Preview6下測試。

       測試結果對比如下:

       我們可以看見,對於test1,C++版本要快很多,對於test2,C#版本和C++版本性能相當,甚至略快。

       為什么會出現這種現象呢?下面來具體分析:

       test1 的循環的賦值是位置無關的,因此,編譯器可以通過SIMD等並行計算指令來優化,test2 的循環的賦值是位置相關的,編譯器很難使用SIMD等並行計算指令來優化。通過上面的結果可以猜測,VC編譯器,對test1進行了並行優化,而.net core 3.0 preview6 沒有對test1 進行並行優化。

       我們來驗證這一猜測。.net core 3.0 提供了對SIMD 指令的支持,下面手動對test1進行並行優化,測試性能:

 

       結果是0.633s,接近於C++版本的0.441s。相對於優化前的2.289s,提速了3倍多。

       同樣的程序,我用 java 8 測試,結果大吃一驚:

 

       test1 耗時 0.654s,和並行優化后的.net core近似,可見 jvm 虛擬機對此進行了並行優化。test2 耗時1.755s,比C++版本和.net core版本都要快,並且差距巨大!

 

       顯然,jvm對test2這種情況進行了特殊關照。要理解這一現象,就需要對Java虛擬機的機制有深入了解。HotSpot 虛擬機里內置了兩個JIT編譯器:Client Compiler和Server Compiler,簡稱為C1編譯器和C2編譯器。C1編譯器將字節碼編譯為本地代碼,進行簡單、 可靠的優化,如有必要將加入性能監控的邏輯。C2編譯會啟用一些編譯耗時較長的優化,甚至進行一些激進優化。

       查找文獻可知,默認情況下,當方法調用次數+循環回邊次數超過10000、計數器是int等幾個簡單類型、步增是常量時,會觸發C2編譯優化。test2恰恰滿足這三種情況!

       下面我們再設計一個實驗,將步增改為變量,看看測試結果:

       由測試可知,將步增改為變量后,測試結果為6.163秒,和C++及 .net core 測試結果近似。

       針對這個測試案例,可以猜測 C2 優化時進行了循環展開。下面,我們在 .net core 下手動展開循環,測試性能,驗證我們的猜想:

 

       測試結果為1.983s,近似java8的1.755s。猜想得到驗證。

 ----

       總結:隨着JVM、.Net等虛擬機技術的發展,語言特性對高性能計算性能影響越來越低,對計算機體系結構、編譯原理、虛擬機編譯機制的理解,對性能的影響變得更為重要。JVM的自動優化做的非常的強悍,.net core 在這方面還有不小差距,不過 .net core 可以通過手工優化來彌補這一差距。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM