第55條:謹(jǐn)慎地進(jìn)行優(yōu)化

有三條與優(yōu)化有關(guān)的格言是每個(gè)人都應(yīng)該知道的。這些格言我們可能已經(jīng)耳熟能詳,但是,如果對(duì)它們還不太熟悉失乾,請(qǐng)看下面:
  很多計(jì)算上的過(guò)失都被歸咎于效率(沒(méi)有必要達(dá)到的效率),而不是任何其他的原因纬乍,——甚至包括盲目地做傻事碱茁。
                      ——William A.Wulf[Wulf72]
  不要去計(jì)校效率上的一些小小的得失,在97%的情況下仿贬。不成熟的優(yōu)化才是一切問(wèn)題的根源纽竣。
                      ——Donald E.Knuth[Knuth74]
  在優(yōu)化方面,我們應(yīng)該遵守兩條規(guī)則:
  規(guī)則1:不要進(jìn)行優(yōu)化。
  規(guī)則2(僅針對(duì)專(zhuān)加):還是不要進(jìn)行優(yōu)化一一也就是說(shuō)蜓氨,在你還沒(méi)有絕對(duì)清晰的未優(yōu)化方案之前聋袋,請(qǐng)不要進(jìn)行優(yōu)化.
                      ——M.A.Jackson[Jackson75]
  所有這些格言都比Java程序設(shè)計(jì)語(yǔ)言的出現(xiàn)早了2 0年。它們講述了一個(gè)關(guān)于優(yōu)化的深刻真理:優(yōu)化的弊大于利语盈,特別是不成熟的優(yōu)化舱馅。在優(yōu)化過(guò)程中,產(chǎn)生的軟件可能既不快速刀荒,也不正確代嗤,而且還不容易修正。
  不要因?yàn)樾阅芏鵂奚侠淼慕Y(jié)構(gòu)缠借。要努力編寫(xiě)好的程序而不足快的欄序干毅。如果好的程序不夠快,它的結(jié)構(gòu)將使它可以得到優(yōu)化泼返。好的程序體現(xiàn)了信息隱藏 (information hiding)的原則:只要有可能硝逢,它們就會(huì)把設(shè)計(jì)決策集中在單個(gè)模塊中,因此绅喉,可以改變單個(gè)決策渠鸽,而不會(huì)影響到系統(tǒng)的其他部分(見(jiàn)第13條:使類(lèi)和成員的可訪(fǎng)問(wèn)性最小化)。
  這并不意味著在完成程序之前就可以忽略性能問(wèn)題柴罐。實(shí)現(xiàn)上的問(wèn)題可以通過(guò)后期的優(yōu)化而得到修正徽缚,但是,遍布全局并且限制性能的結(jié)構(gòu)缺陷幾乎是不可能被改正的.除非重新編寫(xiě)系統(tǒng)革屠。在系統(tǒng)完成之后再改變?cè)O(shè)計(jì)的某個(gè)基本方面凿试,會(huì)導(dǎo)致系統(tǒng)的結(jié)構(gòu)很不好,從而難以維護(hù)和改進(jìn)似芝。因此那婉,必須在設(shè)計(jì)過(guò)程中考慮到性能間題。
  努力避免那些限制性能的設(shè)計(jì)決策党瓮。當(dāng)一個(gè)系統(tǒng)設(shè)計(jì)完成之后详炬,其中最難以更改的組件是那些指定了模塊之間交互關(guān)系以及模塊與外界交互關(guān)系的組件。在這些設(shè)計(jì)組件之中寞奸,最主要的是API線(xiàn)路層(wire-Ievel)協(xié)議以及永久數(shù)據(jù)格式痕寓。這些設(shè)計(jì)組件不僅在事后難以甚至不可能改變,而且它們都有可能對(duì)系統(tǒng)本該達(dá)到的性能產(chǎn)生嚴(yán)重的限制蝇闭。
  要考慮API設(shè)計(jì)決策的性能后果。使公有的類(lèi)型成為可變的(mutable )硬毕。這可能會(huì)導(dǎo)致大量不必要的保護(hù)性拷貝(見(jiàn)第39條:必要時(shí)進(jìn)行保護(hù)性拷貝 )呻引。同樣地,在適合使用復(fù)合模式的公有類(lèi)中使用繼承吐咳,會(huì)把這個(gè)類(lèi)與它的超類(lèi)永遠(yuǎn)地束縛在一起逻悠,從而人為地限制了子類(lèi)的性能(見(jiàn)第16條:復(fù)合優(yōu)先于繼承)元践。最后一個(gè)例子,在A(yíng)PI中使用實(shí)現(xiàn)類(lèi)型而不是接口童谒,會(huì)把你束縛在一個(gè)具休的實(shí)現(xiàn)上单旁,即使將來(lái)出現(xiàn)更快的實(shí)現(xiàn)你也無(wú)法使用(見(jiàn)第52條:通過(guò)接口引用對(duì)象)。
  API設(shè)計(jì)對(duì)于性能的影響是非常實(shí)際的饥伊∠蠡耄考慮java.awt.Component類(lèi)中的getSize方法。這個(gè)決定就是琅豆,這個(gè)注重性能的方法將返回Dimension實(shí)例愉豺,與此密切相關(guān)的決定是,Dimension實(shí)例是可變的茫因,迫使這個(gè)方法的任何實(shí)現(xiàn)都必須為每個(gè)調(diào)用分配一個(gè)新的Dimension實(shí)例蚪拦。盡管在現(xiàn)代V M上分配小對(duì)象的開(kāi)銷(xiāo)并不大,但是分配數(shù)百萬(wàn)個(gè)不必要的對(duì)象仍然會(huì)嚴(yán)重地?fù)p害性能冻押。

    public Dimension getSize() {
        return size();
    }
    @Deprecated
    public Dimension size() {
        return new Dimension(width, height);
    }

在這種情況下驰贷,有幾種可供選擇的替換方案。理想情況下洛巢,Dimension應(yīng)該是不可變的(見(jiàn)第15條:使可變性最小化)括袒;另一種方案是,用兩個(gè)方法來(lái)替換getSize方法狼渊,它們分別返回Dimension對(duì)象的單個(gè)基本組件箱熬。實(shí)際上.在1.2發(fā)行版本中,出于性能方面的原因狈邑,兩個(gè)這樣的方法已經(jīng)被加入到Component API中城须。然而,原先的客戶(hù)端代碼仍然可以使用getSize方法米苹,但是仍然要承受原始API設(shè)計(jì)決策所帶來(lái)的性能影響糕伐。
  幸運(yùn)的是,一般而言良瞧,好的API設(shè)計(jì)也會(huì)帶來(lái)好的性能褥蚯。為獲得好的性能而對(duì)API進(jìn)行包裝赞庶,這是一種非常不好的想法澜薄。導(dǎo)致你對(duì)API進(jìn)行包裝的性能因素可能會(huì)在平臺(tái)未來(lái)的發(fā)行版本中肤京,或者在將來(lái)的底層軟件中不復(fù)存在忘分,但是披包裝的API以及由它引起的問(wèn)題將永遠(yuǎn)困擾著你。
  一旦謹(jǐn)慎地設(shè)計(jì)了程序熬荆,井且產(chǎn)生了一個(gè)清晰累盗、簡(jiǎn)明若债、結(jié)構(gòu)良好的實(shí)現(xiàn),那么就到了該考慮優(yōu)化的時(shí)候了傲须,假定此時(shí)你對(duì)于程序的性能還不滿(mǎn)意泰讽。
  回想一下Jackson的兩條優(yōu)化規(guī)則:“不要優(yōu)化"以及“(僅針對(duì)專(zhuān)家)還是不要優(yōu)化"。他可以再增加一條:在每次試圖做優(yōu)化之前和之后,要對(duì)性能進(jìn)行測(cè)量永乌。你可能會(huì)驚訝于自己的發(fā)現(xiàn)翅雏。試圖做的優(yōu)化通常對(duì)于性能井沒(méi)有明顯的影響望几,有時(shí)候甚至?xí)剐阅茏兊酶睢V饕脑蛟谟诼ナ模鲁龀绦虬褧r(shí)間花在哪些地方并不容易名挥。你認(rèn)為程序慢的地方可能井沒(méi)有問(wèn)題榄融,這種情況下實(shí)際上是在浪費(fèi)時(shí)間去嘗試優(yōu)化愧杯。大多數(shù)人認(rèn)為:程序把8 0%的時(shí)間花在2 0%的代碼上了力九。
  性能剖析工具有助于你決定應(yīng)該把優(yōu)化的重心放在哪里跌前。這樣的工具可以為你提供運(yùn)行時(shí)的信息,比如每個(gè)方法大致上花費(fèi)了多少時(shí)間臂寝、它被調(diào)用多少次。除了確定優(yōu)化的重點(diǎn)之外帚呼,它還可以警告你是否需要改變算法眷蜈。如果一個(gè)平方級(jí)(或更差)的算法潛藏在程序中酌儒,無(wú)論怎么調(diào)整和優(yōu)化都很難解決問(wèn)題。你必須用更有效的算法來(lái)替換原來(lái)的算法。系統(tǒng)中的代碼越多,使用性能剖析器就顯得越發(fā)重要酝润。這就好像要在一堆干草中尋找一根針:達(dá)堆干草越大,使用金屬探測(cè)器就越有用璃弄。JDK帶了簡(jiǎn)單的性能剖析器要销,現(xiàn)代的IDE也提供了更加成熟的性能剖折工具。
  在Java乎臺(tái)上對(duì)優(yōu)化的結(jié)果進(jìn)行測(cè)量夏块,比在其他的傳統(tǒng)平臺(tái)上更有必要疏咐,因?yàn)镴ava程序設(shè)計(jì)語(yǔ)言沒(méi)有很強(qiáng)的性能模型 。各種基本操作的相對(duì)開(kāi)銷(xiāo)也沒(méi)有明確定義脐供。程序員所編寫(xiě)的代碼與CPU執(zhí)行的代碼之間存在“語(yǔ)義溝(semantic gap)”浑塞,而且這條語(yǔ)義溝比傳統(tǒng)編譯語(yǔ)言中的更大,這使得要想可靠地預(yù)測(cè)出任何優(yōu)化的性能結(jié)果都非常困難政己。大量流傳的關(guān)于性能的說(shuō)法最終都被證明為半真半假酌壕。或者根本就不正確歇由。
  不僅Java的性能模型未得到很好的定義卵牍。而且在不同的JVM實(shí)現(xiàn).,或者不同的發(fā)行版本沦泌,以及不同的處理器糊昙,在它們這些當(dāng)中也都各不相同。如果將要在多個(gè)JVM實(shí)現(xiàn)和多種硬件平臺(tái)上運(yùn)行程序谢谦,很重要的一點(diǎn)是释牺,需要在每個(gè)Java實(shí)現(xiàn)上測(cè)量?jī)?yōu)化效果萝衩。有時(shí)候,還必須在從不同JVM實(shí)現(xiàn)或者硬件平臺(tái)上得到的性能結(jié)果之中進(jìn)行權(quán)衡没咙。
  總而言之猩谊,不要費(fèi)力去編寫(xiě)快速的程序——應(yīng)該努力編寫(xiě)好的程序,速度自然會(huì)隨之而來(lái)祭刚。在設(shè)計(jì)系統(tǒng)的時(shí)候预柒,特別是在設(shè)計(jì)API、線(xiàn)路層協(xié)議和永久數(shù)據(jù)格式的時(shí)候袁梗,一定要考慮性能的因素。當(dāng)構(gòu)建完系統(tǒng)之后憔古,要測(cè)量它的性能遮怜。如果它足夠快,你的任務(wù)就完成了鸿市。如果不夠快锯梁,則可以在性能剖析器的幫助下,找到問(wèn)題的根源焰情,然后設(shè)法優(yōu)化系統(tǒng)中相關(guān)的部分陌凳。第一個(gè)步驟是檢查所選擇的算法:再多的低層優(yōu)化也無(wú)法彌補(bǔ)算法的選擇不當(dāng)。必要時(shí)重復(fù)這個(gè)過(guò)程内舟,在每次改變之后都要測(cè)量性能合敦,直到滿(mǎn)意為止。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末验游,一起剝皮案震驚了整個(gè)濱河市充岛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耕蝉,老刑警劉巖崔梗,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異垒在,居然都是意外死亡蒜魄,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)场躯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谈为,“玉大人,你說(shuō)我怎么就攤上這事推盛÷透螅” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵耘成,是天一觀(guān)的道長(zhǎng)榔昔。 經(jīng)常有香客問(wèn)我驹闰,道長(zhǎng),這世上最難降的妖魔是什么撒会? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任嘹朗,我火速辦了婚禮,結(jié)果婚禮上诵肛,老公的妹妹穿的比我還像新娘屹培。我一直安慰自己,他們只是感情好怔檩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布褪秀。 她就那樣靜靜地躺著,像睡著了一般薛训。 火紅的嫁衣襯著肌膚如雪媒吗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天乙埃,我揣著相機(jī)與錄音闸英,去河邊找鬼。 笑死介袜,一個(gè)胖子當(dāng)著我的面吹牛甫何,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播遇伞,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼辙喂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了赃额?” 一聲冷哼從身側(cè)響起加派,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎跳芳,沒(méi)想到半個(gè)月后芍锦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡飞盆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年娄琉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吓歇。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡孽水,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出城看,到底是詐尸還是另有隱情女气,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布测柠,位于F島的核電站炼鞠,受9級(jí)特大地震影響缘滥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谒主,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一朝扼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧霎肯,春花似錦擎颖、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至懂缕,卻和暖如春异旧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背提佣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荤崇,地道東北人拌屏。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像术荤,于是被迫代替她去往敵國(guó)和親倚喂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,111評(píng)論 25 707
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理瓣戚,服務(wù)發(fā)現(xiàn)端圈,斷路器,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法子库,類(lèi)相關(guān)的語(yǔ)法舱权,內(nèi)部類(lèi)的語(yǔ)法,繼承相關(guān)的語(yǔ)法仑嗅,異常的語(yǔ)法宴倍,線(xiàn)程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,631評(píng)論 18 399
  • 作女人,我希望自己活得像一棵樹(shù)而不是一朵花仓技。淵博的學(xué)識(shí)筑就基根鸵贬,豐富的閱歷錘煉氣場(chǎng),日后的年月里才能在萬(wàn)難塵世中堅(jiān)...
    山靜無(wú)人水自流閱讀 1,377評(píng)論 0 0
  • iOS10正式版本已發(fā)布脖捻,Xcode8也跟著就發(fā)布了阔逼,于是我就在第一時(shí)間將Xcode8和iOS10都更新了。但是一...
    Jvaeyhcd閱讀 2,256評(píng)論 1 6