原文:
http://www-128.ibm.com/developerworks/cn/java/j-jtp03253/
知道何時優化比知道如何優化更重要
Brian Goetz
首席顧問, Quiotix Corp
2003 年 6 月 21 日
性能問題從何而來?導致性能問題的編程選項有許多類型 — 效率低的算法、冗餘計算、糟糕的資源分配和使用情況、過多的同步,或者很明顯就是效率低的設計。但更普遍的且具有破壞性的要屬管理和方法錯誤,而不是編程錯誤。在這一部分的 Java 理論與實踐中,Brian Goetz 將討論在項目中使用 Java 語言時遇到的一些最常見的性能錯誤。在附帶的 論壇中與作者和其他讀者分享您關於本文的心得。(您也可以通過單擊本文頂部或底部的 討論來訪問該論壇。)
性能管理通常被視為一種巫術,因為性能問題通常在應用程序開發完成之後才會出現。到那時,就難以確定它們的根源。然而,一旦十分準確地確定瞭性能問題的起因,那麼修正它常常是比較簡單的事情。工程師在尋找更有效的方法來執行特殊任務方面通常具有相當的創造性(有時他們的創造性過瞭頭)。對於任何給定的性能問題,通過使用高速緩存來減少冗餘計算或者隻是添加更多的硬件,解決方案可能會與用更有效的算法進行替換一樣簡單。但是,要清楚地確定性能問題的根源會很困難,而設計復雜程序甚至 更加困難,所以首先要使它們沒有性能問題。
雖然編程決策 — 如算法或數據表示的次優(sub-optimal)選擇,無法重用先前計算的結果,或者糟糕的資源管理 — 通常被認為是性能問題的直接起因,但大多數性能問題還有一個更深的起因:無法首先將性能管理、目標和測量集成到開發過程中。
問題?什麼問題?
您如何知道何時有性能問題呢?對於大多數開發團隊,回答(用美國高級法院法官 Potter Stewart 對罪行描述的話來講)是:在遇到時才知道。這是問題的核心 — 性能目標、度量和測量常常沒被考慮到,直到發現時,已經太晚瞭。
最常見的性能管理策略是……也沒什麼,它通常采用下列兩種形式之一:
在應用程序開發完成之前完全忽略性能
開發時進行優化,這通常意味著隻關註極微小的性能考慮事項,而忽略較大的方面
這兩種策略共有同一個基本問題 — 它們沒有將性能管理視作開發過程的一個集成部分。
有缺陷的性能策略 A:完全忽略性能
第一種方法是完全忽略性能,該方法將性能視作可以在項目結束時處理的事情,比如象編寫發行說明或構建安裝程序。該策略基本上靠運氣,因為計算機運算速度一年比一年快,所以在性能方面總能夠應付得過去。
這種靠運氣的問題(即使當成功的可能性非常大時)就是:當出現性能問題時,您沒有處理它、確定其根源或以特別方式解決它的框架。您也沒有在開發規劃中安排用於性能測量和調優的時間。這有點象網站,除瞭安裝具有缺省配置的防火墻外,沒有嘗試使用一個連貫的安全性策略,然後發現它們已經被竊取。您從何處著手呢?
有缺陷的性能策略 B:開發時進行優化
另一個常見但甚至更糟的方法是讓極微小的性能考慮事項驅動體系結構和設計決策。開發人員喜歡優化代碼,其正當理由就是它令人滿意和帶來樂趣。但是,就關註的代碼以及在開發周期中解決性能問題的時機而言,知道何時優化更為重要。遺憾的是,開發人員一般無法憑直覺判斷性能問題將實際出現在應用程序的哪個位置。結果,他們浪費瞭大量精力對很少執行的代碼路徑進行優化,或者更糟的是,他們損害好的設計和開發實踐來優化早先沒有任何性能問題的組件。當您埋頭編寫代碼時,很容易在性能問題上隻見樹而不見林。
使各個代碼路徑盡可能快地運行並不保證最終產品會很好地執行。再怎麼進行局部優化都不可能彌補根本上效率低的設計,即使將每個組件實現為盡可能的快,也是如此。開發時優化策略用關註低級別的性能考慮事項來替代對整個項目實施的性能策略,而且讓您無法確信您真正有一個性能策略。
開發時進行優化的許多問題之一是它忽略瞭優化中的固有風險。少數優化也會使設計更佳,錯誤更少,但這些都是例外情況。通常,優化涉及性能和其它考慮事項(如幹凈的設計、明確性、靈活性和功能性)之間的權衡。優化會付出一定的成本和風險:它可能會引入錯誤、限制代碼的功能性或者使使用或維護變得更加困難。在承受這些成本之前,請確保值得這樣做。
將性能管理作為開發過程的一部分
從一開始就應該將性能測量和規劃集成到開發過程中,對開發和性能測量和調優進行單獨、交錯的迭代。這意味著設定性能目標、準備性能測量方案以及在開發代碼時經常復查代碼性能。最好將舊的測試結果保存在數據庫中,這樣您可以容易地比較當您更改代碼時性能的變化情況。
對開發和性能進行單獨的迭代,可讓您在開發迭代期間集中精力編寫起作用的無錯誤的代碼,如果需要的話,您還會知道不久將有一個適當的機會來改進性能。如果您想起一個使代碼運行更快的巧妙竅門,那麼在代碼中加一個註釋以詳細描述您的想法,但現在不要實現!現在不是進行優化的時候。 如果最終必需這麼做,則當您關註性能時再返回來。性能優化應該由性能目標驅動,並受性能測量支持。別的東西不過是“次要的”。
測量兩次,然後再測量幾次
測量是性能管理的關鍵元素。想一下:一個給定的妙計將使代碼運行得更快嗎?我們準備驗證這一點。在實施妙計的 前後,使用性能測量工具來測試性能。如果您沒有測到有改進,該怎麼辦?那麼,準備收回您的妙計。如果您測不出有好處,那為什麼要冒破壞工作代碼的風險呢?
在性能迭代期間,測量應用程序或其組件的性能,並將它們與先前迭代的測量比較。有些方面慢下來瞭嗎?找出原因。如果它達不到性能目標,那麼您不一定非得更改它,但現在您已經獲得瞭有價值的、有關您更改後對性能影響的反饋信息。
達到目標瞭嗎?
如果您沒有定量的性能目標和支持它們的測量規劃,那麼性能調優似乎是無意義的。您如何知道您已經達到目標瞭?其它開發階段(如編碼、測試和打包)都定義瞭目標 — 實現這組功能、修正這些錯誤等等。性能階段也應該有結構和目標。
當對性能的關註是源自於外部時(不管是客戶還是公司內的另一個部門),具有性能目標尤為重要。當某人告訴您使程序運行得更快時,您應該先問一下“我必須使它快多少?”否則,您可能會在調優方面投入過多的資源,但仍不能使客戶滿意。投入十二分的精力來使程序運行速度提高 30%,不料卻有人反應“哎呀,我原希望速度能提高 50%。”,這會讓人很失望。
結束語
性能管理不僅包括優化,還包含許多其它東西。它有一個用於決定何時優化何時不優化的框架。您應該根據明確的性能目標、測量和規劃來做這些決策,而不是直覺。
參考資料
您可以參閱本文在 developerWorks 全球站點上的 英文原文.
參與有關本文的 論壇。(您也可以通過單擊本文頂部或底部的 討論來訪問該論壇。)
Jack Shirazi 撰寫的 Java Performance Tuning (OReilly & Associates,2003)一書包含瞭大量有關個別優化以及如何測量性能和智能地處理性能調優的建議。
Jack Shirazi 的 Java Performance Tuning網站包含瞭許多與性能相關的文章和技巧的鏈接。
由 Steve Wilson 與 Jeff Kesselman 合著的 Java Platform Performance: Strategies and Tactics (Addison-Wesley,2000)包含瞭一些對在 Java 類庫開發期間遇到的實際性能問題的深入案例研究。
另一個由 Brian Goetz 撰寫的第三方“ Design for Performance”系列文章( JavaWorld)提供瞭有關設計決策如何影響性能的一些技巧。
文章“ Spotlight on Java performance”( developerWorks,2001 年 12 月)說明瞭一個用於 IBM Java 虛擬機項目的明智且實用的性能管理策略。
“ Optimize your Java applications performance”( developerWorks,2002 年 6 月)演示瞭一個迭代式性能調優策略,它使用算法改進、高速緩存和低級別性能調優。
在 developerWorksJava 技術專區 可找到數百篇有關 Java 技術的參考資料。
關於作者
Brian Goetz 是一位軟件顧問,在過去的 15 年裡一直是一位專業軟件開發人員。他是 Quiotix的首席顧問,該公司是位於加利福尼亞 Los Altos 的軟件開發和咨詢公司。請在業界流行的出版物上查閱 Brian 已發表的和即將發表的文章。可以通過 brian@quiotix.com與 Brian 聯系。