本節書摘來自異步社群出版社《c++程式設計規範:101條規則、準則與最佳實踐》一書中的第2章,第2.4節,作者:【加】herb sutter , 【羅】andrei,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。
摘要
拉丁諺語雲,快馬無需鞭策:不成熟優化的誘惑非常大,而它的無效性也同樣嚴重。優化的第一原則就是:不要優化。優化的第二原則(僅适用于專家)是:還是不要優化。再三測試,而後優化。
讨論
正如[stroustrup00]§6開始所引用的優美名言說的那樣:
不成熟的優化是萬惡之源。——donald knuth (引用hoare的話)
另一方面,我們不能忽視效率。——jon bentley
hoare和knuth當然而且永遠是完全正确的(見第6條和本條)。bentley亦然(見第9條)。
我們将不成熟的優化定義為這樣的行為:以性能為名,使設計或代碼更加複雜,進而導緻可讀性更差,但是并沒有經過驗證的性能需求(比如實際的度量資料和與目标的比較結果)作為正當理由,是以本質上對程式沒有真正的好處。毫無必要而且無法度量的優化行為其實根本不能使程式運作得更快,這種情況簡直是太常見了。
請永遠記住:
讓一個正确的程式更快速,
比讓一個快速的程式正确,要容易得太多、太多。
是以,預設時,不要把注意力集中在如何使代碼更快上;首先關注的應該是使代碼盡可能地清晰和易讀(見第6條)。清晰的代碼更容易正确編寫,更容易了解,更容易重構——當然也更容易優化。使事情複雜的行為,包括優化,總是以後再進行的——而且隻在必要的時候進行。
不成熟的優化經常并不能使程式更快,這主要有兩方面原因。一方面,我們程式員在估計哪些代碼應該更快或者更小,以及代碼中哪裡會成為瓶頸上名聲很臭。包括本書的作者,也包括讀者你。考慮一下這些事實吧:現代計算機都具有極為複雜的計算模型,經常是幾個流水線處理單元并行工作,深高速緩存層次結構,猜測執行(speculative execution)[3],分支預測……這還隻是cpu晶片。在硬體之上,編譯器也在盡其所能地猜測,将源代碼轉換為最能發掘硬體潛力的機器碼。而在這些複雜的架構之上,還有……還有你——程式員的猜測。是以,如果隻是猜測的話,你的那些目标不明确的微觀優化就很難有機會顯著地改善代碼。是以,優化之前必須進行度量;而度量之前必須确定優化的目标。在需求得到驗證之前,注意力應該放在頭号優先的事情上——為人編寫代碼。(當有什麼人要求你進行優化的時候,請進行需求驗證。)
另一方面,在現代程式中,許多操作越來越不受cpu的限制。它們可能更受記憶體的限制、網絡的限制、硬碟的限制,需要等待web service,或等待資料庫。即使在最好的情況下,優化這些操作的應用程式代碼,也隻不過能使等待操作更快。這也意味着程式員浪費了寶貴的時間去改善沒有必要改善的地方,卻沒有進行需要的有價值的改善。
當然,遲早有一天需要優化某些代碼。到那時,首先要考慮算法優化(見第7條),并嘗試将優化封裝和子產品化(比如,用一個函數或者類,見第5條和第11條),然後在注釋中清楚地說明優化的原因并列出所用算法作為參考。
初學者常犯的一個錯誤,就是編寫新代碼時着迷于進行過度優化(而且充滿自信),卻犧牲了代碼的可了解性。這常常會産生大雜燴代碼,這種代碼即使開始時是正确的,也非常難以閱讀和修改。(見第6條。)
通過引用傳遞(見第25條),優先調用字首形式的++和--(見第28條),和使用很自然地從指尖流出的慣用法,都不屬于不成熟的優化。這些都不是不成熟的優化,而是在避免不成熟的劣化(見第9條)。
示例
例inline悖論。這個例子簡單闡述了不成熟的微觀優化所帶來的隐性代價。分析器(profiler)能夠通過函數的命中計數出色地告訴我們哪些函數應該但是沒有标記為inline;然而,分析器在尋找哪些函數已經标記為inline但是不應該标記方面,卻極不擅長。太多的程式員習慣以優化的名義“将inline作為預設選擇”,這幾乎總是以更高的耦合性為代價,而換來的好處到底如何卻很可疑。(這裡有一個前提,編寫inline在所用的編譯器上确實起作用。參閱[sutter00]、[sutter02]和[sutter04]。)
例外情況
在編寫程式庫的時候,預測哪些操作最後會用于性能敏感的代碼中更加困難。但即使是程式庫的編寫者,在實施容易令人糊塗的優化之前,也會對很大範圍内的客戶代碼進行性能測試。
參考文獻
[bentley00] §6 ● [cline99] §13.01-09 ● [kernighan99] §7 ● [lakos96] §9.1.14 ● [meyers97] §33 ● [murray93] §9.9-10, §9.13 ● [stroustrup00] §6 introduction ● [sutter00] §30, §46 ● [sutter02] §12 ● [sutter04] §25