- 現代CPU性能分析與優化
- (美)丹尼斯·巴赫瓦洛夫
- 2208字
- 2023-11-09 19:34:48
1.1 為什么需要性能調優
現代CPU的核數量每年都在增長,到2019年底,我們可以購買到具有100多個邏輯核的高端服務器處理器。雖然令人驚嘆,但這并不意味著我們無須關心性能問題,我們經常看到的情況是應用程序性能可能不會隨著CPU核數量的增加而提升。典型的通用多線程應用程序的性能并不總是隨著分配到任務的CPU核數量的增長而線性增長,了解發生這種情況的原因及可能的解決方案對產品的未來發展至關重要。產品性能若不能被恰當地分析和調優,可能會導致大量的性能和資金浪費,甚至可能導致產品最終失敗。
據論文(Leiserson et al.,2020)介紹,至少在近期,大部分應用程序的性能提升都源自軟件棧。但是很不幸,應用程序并不會默認得到最優的性能。在論文(Leiserson et al.,2020)中,作者提供了一個很好的例子,描繪了在源代碼層面進行性能提升的潛力。表1總結了兩個4096×4096矩陣相乘的程序經性能工程優化后的加速效果。經過多種優化后的最終結果是程序運行速度提升了60000多倍。舉這個例子并不是為了讓你選擇Python或者Java語言(它們都是非常優秀的編程語言),而是為了打破默認情況下軟件就有“足夠好”性能的印象。
表1 通過性能工程加速兩個4096×4096矩陣相乘的程序

注:運行于雙插槽60 GB內存Intel Xeon E5-2666 v3系統上,摘自論文(Leiserson et al.,2020)。
以下是影響系統在默認情況下獲得最佳性能的一些重要因素:
1. CPU的限制 人們經常會忍不住問:“硬件為何不能解決這一切性能問題?”現代CPU以驚人的速度執行指令,并且每一代都在變得更好。但是,如果執行任務的指令不是最優的甚至是多余的,CPU也無能為力,處理器并不能神奇地把次優的代碼轉化為性能更好的代碼。例如,如果我們用冒泡算法BubbleSort實現排序程序,CPU無法識別出它是排序算法的實現并替換為更好的算法(如快速排序算法QuickSort)。CPU盲目地執行被告知的任務。
2.編譯器的限制 “這不是編譯器該干的事情嗎?為什么編譯器沒有解決所有的問題?”不錯,當今的編譯器非常智能,但是仍然會生成次優的代碼。編譯器很擅長消除冗余,但是當需要對諸如函數內聯、循環展開等做出更復雜的決定時,編譯器也許不能生成最佳的代碼。例如,對于編譯器是否應當將函數內嵌到調用它的代碼中,并沒有二選一的“是”或“否”的答案,而是依賴編譯器對多種因素的綜合考量。通常,編譯器會根據復雜的成本模型和啟發式方法進行判斷,但這不能保證在所有可能場景下都正確。此外,編譯器只會在確保安全且不影響生成的機器碼正確性的情況下做代碼優化。對編譯器開發者來說,要在所有可能情況下讓某些優化操作生成正確的機器碼是非常困難的,所以他們通常采取保守策略以避免進行某些優化[4]。最后,編譯器通常不會改變程序使用的數據結構,因為數據結構對性能至關重要。
3.算法復雜度分析的限制 開發者經常過度關注算法復雜度分析,進而導致他們傾向于采用復雜度最優的流行算法,即使對給定問題而言它可能并不是性能最優的。例如,對于兩個排序算法ⅠnsertionSort和QuickSort,如果采用大O來度量,一般而言后者會勝出:ⅠnsertionSort的時間復雜度是O(N2),而QuickSort的復雜度是O(NlogN)。當N相對較小[5]時,ⅠnsertionSort比QuickSort表現更好。復雜度分析無法解釋各種算法的所有分支預測和緩存的影響,所以只是將它們封裝成一個隱含的常數C,有時這會對性能產生巨大的影響。不經過對目標負荷的測試而盲目地信任大O度量,會讓開發者誤入歧途。對某個問題,即使最知名的算法也不一定能在所有輸入情況下都性能表現最佳。
上述限制為軟件性能調優以充分發揮其潛力提供了空間。廣義來說,軟件棧包含很多層,例如,固件、BIOS、操作系統、函數庫和應用程序源代碼。但是,由于大多數底層軟件都不在我們的直接控制范圍內,因此我們主要聚焦在源代碼上。另外一個經常接觸的軟件組件是編譯器,通過各種注解,編譯器可以生成讓程序性能顯著提高的機器碼,本書將給出很多這樣的例子。
個人經驗 即使你并不是編譯器專家,也能成功地提升應用程序的性能。根據我的經驗,至少90%的轉換可以在源代碼層面完成,而無須深入研究編譯器源代碼。盡管如此,理解編譯器的工作原理,以及懂得如何能夠讓編譯器為你所用還是非常有幫助的。
此外,在當前單線程性能已經達到峰值并趨平時,把應用程序分布式運行于多個計算核是很有必要的。而這就要求程序的不同線程之間能高效通信、排除不需要的資源消耗并規避多線程程序的典型問題。
特別需要指出的是,性能的提升不止來自軟件調優。據論文(Leiserson et al.,2020)介紹,未來另外兩個主要潛在加速源是算法(尤其對機器學習等新問題領域)和高效的硬件設計。顯然,算法對應用程序性能有顯著的影響,但是在本書中我們不討論這個主題。因為大多數時候,軟件工程師都是在現有硬件平臺上開發,所以我們也不討論新的硬件設計。當然,理解現代CPU設計對應用程序調優很重要。
“在后摩爾定律時代,讓代碼運行得更快(尤其根據運行它的硬件進行定制)變得更加重要。”(Leiserson et al.,2020)
本書中的方法論聚焦于從應用程序中擠出最后一點性能潛力,這類轉換方法可以參考表1中的第6行和第7行。將要討論的優化方法對性能的提升通常不會很大,一般不超過10%,但是千萬不要低估這10%的提升的重要性,特別是對那些運行在云環境下的大型分布式應用程序。根據文獻(Hennessy,2018)的介紹,在2018年,谷歌在運行云的計算服務器上實際花費的費用幾乎與其在電力和冷卻基礎設施上的花費相同。能源效率是一個很重要的問題,而它可以通過優化軟件來改善!
“在這樣的規模下,理解性能特征變得很關鍵——即使性能或利用率上很小的改善也可以轉化為巨大的成本節約。”(Kanev et al.,2015)
- Effective C#:改善C#代碼的50個有效方法(原書第3版)
- Cocos2d-x游戲開發:手把手教你Lua語言的編程方法
- Magento 2 Development Cookbook
- 深度強化學習算法與實踐:基于PyTorch的實現
- Access 2010數據庫應用技術(第2版)
- Unity UI Cookbook
- 深入分布式緩存:從原理到實踐
- Windows Phone 7.5:Building Location-aware Applications
- D3.js By Example
- Apache Camel Developer's Cookbook
- Python網絡爬蟲技術與應用
- Practical Predictive Analytics
- Robot Framework Test Automation
- Building UIs with Wijmo
- Java EE輕量級解決方案:S2SH