2020-05-27 09:41:00
本文來源「優達學城」
原作:Ravi Shankar Rajan ,譯者:歐剃
“讓這代碼跑得快一點!!”——我碰到的第一件代碼優化任務就是這么開始的,那個專案是一個巨大的 SAP 云平臺應用程式,總共含有超過 3 萬行的代碼,
整個 App 加載資料的程序非常之慢,顯然用戶并不喜歡這種體驗,
然而,我必須承認,這個專案的代碼寫的挺不錯,資料庫呼叫很合適,只在有需要的地方進行回圈,模組化也實作的很到位,我花了兩天時間,絞盡腦汁地進行各種測驗,審查代碼邏輯,但完全沒發現到底是什么地方讓這個程式變得如此之慢,
就在第三天,在我窮盡了所有的辦法,最后一點理智也快要消失的時候,我終于發現了問題所在,
在其中的一個讀取頁面上,被塞了一個等待陳述句,程式到這里就停上 20 秒,

這大約是原來除錯這段代碼的程式員在排查的程序中插入的等待命令,結果在將代碼合并進生產環境的時候忘記把這行東西去掉了,而在生產代碼中,每次呼叫讀取的時候,這段等待命令都會被執行,這就進一步放大了產生的問題,
于是,我把這行代碼刪掉了,好家伙,一切都正常了!
有人說,代碼優化是一把雙刃劍
優化你的軟體是一件好事,但這并不能保證它永遠都會有好結果,
如你是在錯誤的原因驅動下,或是通過錯誤的方法進行代碼優化,這種所謂的優化往往可能增加成本,級訓生產速度,甚至可能會讓軟體的質量下降,
此外,大多數時候,優化并不是沒有代價的,你必須做出謹慎的權衡,例如,提高速度可能會使你在資源利用方面付出代價,更高效地利用存盤則很容易減慢運行速度,你需要仔細考慮你在其他方面做出的權衡,這樣你的軟體才能夠實作它的主要目標,
也許你會問,那我該怎么辦?
下面是一些值得你考慮的要點,遵循這些原則,可以讓你的代碼更具回應性,也能減少你給用戶的設備以及它們連接到的資料庫帶來的額外壓力,
- 不要進行優化
代碼優化的第一條原則就是,“不要”優化它,
這個程式是不是已經足夠好了?你要去理解這個程式將會被如何使用,知道它是在怎樣的環境下運行的,明白如果讓它運行的更快到底有沒有好處,在真正開始代碼優化之前,你必須要問自己這幾個問題,
沒錯,代碼優化所耗費的經歷和成本,只有在這樣的情況下是有意義的:
-
這個軟體很重要
-
它運行的確實很慢
-
在保證代碼健壯、正確、清楚的情況下,它確實還有改進的余地
一個程式,就算它運行得再快,如果無法得到正確的結果,那就毫無用處,有效的優化,給軟體帶來的好處應該總要比壞處多,但如果你的優化走錯了路,那往往還不如別動它,
所以,你要做的第一件事,應該是設定一個合理的優化目標:
-
你需要清楚地了解你要達成的目標是什么,以及各種優化手段與這個目標之間的關系,
-
你需要明確而簡單地說明這個目標,簡單到就算技術理解能力最差的部門經理也能夠理解和復述它,
-
你需要在整個程序中堅持這些目標,
要開始這項作業,最好的辦法是,根據對目標的影響確定每項任務的優先順序,你要做的每一件事情,都必須是可計量的,不要相信直覺,它基本上總是把你引向非常糟糕的方向,
2. 使用一個分析器
在沒有經過分析之前,不要貿然調整任何東西,最常見的錯誤做法就是,花了一整天去重構優化一段代碼,結果在運行的時候發現,這段代碼平時根本用不到,
分析器能精確地測量出你的程式把時間都花在什么步驟上了,有些分析器能列出每一個函式,包括它們被呼叫的次數,以及每次執行的時候耗時的占比等,
還有的分析器能列出每個命令的執行次數,被頻繁執行的那些命令,在總占用時間上的權重肯定更高,而完全沒被運行的那些命令,往往就是一些無用的代碼,或者沒有經過合適測驗的代碼,
一個好的分析工具,最有用的地方就是能讓你發現軟體中的“熱點”,也就是消耗了最多運行時間的那些函式或者命令陳述句,基本上如果你發現了一個熱點,你也就發現了問題所在,
性能分析的最佳使用方法就是識別出“熱點”,然后盡可能地優化它們,接著再次測量,以查看是不是有新的熱點冒了出來,性能分析的之前分享過很多,關注微信公眾號Java技術堆疊可以查找閱讀,
3. 啟用編譯器優化
通常情況下,有種比較靠譜的優化方式,那就是打開編譯器提供的那些內置的優化選項,
編譯器優化通常會給你的程式帶來幾個百分點到兩倍的運行速度提升,但某些情況下,這也可能反而降低速度,所以你需要在最終交付之前仔細測量性能優化的結果,不過總的來說,現代的編譯器在這方面已經做的足夠好了,程式員基本上再也不需要像以前那樣,不停地對編譯引數做各種頻繁的小調整,
一些現代的編譯器還具備全域優化能力,可以分析你的整個程式,以獲得潛在的提升,如果你的系統中有這樣的編譯器,請一定要試試,它可能會把運行時間減少個幾秒鐘,
注意:編譯器的優化設定越激進,最終編譯出來的程式中出現不明 Bug 的可能性也越高,所以,強烈建議你在開啟編譯器的優化選項后,務必重新進行回歸測驗,以避免出現一些奇怪的意外,
4. 調整代碼
只有到這時,你才真正開始修改調整代碼,在此之前,你必須已經通過第二步的性能分析發現了“熱點”,并且試過使用編譯器進行優化——畢竟絕大多數這些問題能讓編譯器幫你解決,也避免了你把這些代碼弄得過于復雜,
那么,一般來說,有幾種比較成熟的方法來處理這些“熱點”,再次提醒,你必須非常謹慎,確保在提交每個更改之前,對它產生的影響進行測量,
那么,讓我們看看這幾個方法吧,
將常用的運算式計算歸集在一起
如果同一個非常消耗性能的計算在多個地方重復出現,最好能只在一個地方進行計算,然后記住計算結果,除非必要,否則不要在回圈中進行這樣的計算,
用簡單的計算代替消耗性能的演算法
字串處理對于任何一個程式來說,都算是非常常見的運算了,但如果你用錯誤的辦法去處理字串,它們也有可能消耗大量的性能,類似的,在某些情況下,你可以用一系列移位操作來代替乘法運算,
但請務必注意,這種方式或許能帶來一些性能提升(其實并不一定),也有可能讓你寫出非常崎嶇復雜的代碼,所以在重構的時候,你必須非常注意代碼可讀性,以免寫出無法維護的代碼,
消滅回圈
回圈,往往是開銷最大的行為,沒有之一,在允許的情況下(例如迭代數量不太多的時候),盡量避免使用回圈,
快取常用的值
快取能有效地利用本地性——也就是程式(以及用戶)更傾向于重用最近的資料,你只需要快取最常用的字符或資料,就能大大提高程式的性能,
使用一種更低層次的語言重寫
警告:不到萬不得已,不要這樣玩,
更低層次的語言在利用硬體設備性能方面往往更具效率(看看 Python 里的內置函式是用 C 寫的就知道了),但要寫好這些東西,將會消耗更多的編程開發時間,
有時,通過用低層次的編程語言重寫關鍵代碼,能獲得較大的性能提升,但這是以降低可移植性為代價的,也會讓以后的維護變得非常困難,因此,請謹慎做出決定,
請記住:在優化作業中,做出選擇這件事占了90%的權重,值得花時間來決定你要做什么,以及怎樣才能做的對,當然,這也正是編程的黑科技之處!
5. 在你的管理模型中加入代碼審查環節
這條是同時寫給開發者和管理者的,對于軟體工程的管理者,你必須確保代碼審查是專案開發程序的一部分;對于開發者,你應當將代碼審查作為最佳編程做法中的必備環節,
推薦看這篇:基于 Gitlab 的代碼審查,
低效的代碼不會對系統的日常運行造成太大影響,由于這個明顯的理由,我們往往會傾向于讓效率低下的代碼通過審查——因為它并沒有產生任何真正的傷害,不是嗎?這可不對,隨著時間的推移,代碼效率將會越來越低下,并且導致執行速度變慢,最終使客戶端的處理時間大大超過可以接受的范圍,
是的,引入常規代碼檢查,洗掉效率低下的代碼片段,或許會給你增加許多作業量,但從長遠來看,如果你把那些低效的代碼留在原地,未來你將不得不付出成倍的作業量,去檢查為什么代碼的運行要花上這么長的時間——那時的你一定會感激現在的自己,所以說,不要讓現在的偷懶成為你未來的痛苦,盡可能檢查并優化你的代碼效率,
一定要讓別人檢查你的代碼,理想的情況下,檢查者是你所欽佩的某個大佬,但基本上任何開發者都能互相檢查,不過,如果某人根本看不懂你的某些代碼,那可要非常警惕了——要么是檢查者的水平問題,要么就是你的代碼可讀性實在太爛了,
結語
最后,任何代碼的改進,都是從你自身開始的,在編程的世界里,你不可能從第一遍就非常完美地寫出代碼,你總需要對代碼進行更改、修正錯誤,甚至有時代碼無論如何都無法按照你想要的方式作業,
這沒什么問題,這完全就是成為一名程式員的必經之路,讓寫出干凈的代碼,成為你的習慣吧,
正如極限編程的創始者,設計模式的先驅肯特·貝克(Kent Beck)指出的那樣:“我不是一個偉大的程式員,我只是一個不錯的程式員,加上偉大的習慣,”
近期熱文推薦:
1.Java 15 正式發布, 14 個新特性,重繪你的認知!!
2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!
3.我用 Java 8 寫了一段邏輯,同事直呼看不懂,你試試看,,
4.吊打 Tomcat ,Undertow 性能很炸!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/223881.html
標籤:Java
