天天看點

代碼優化 5 大原則,第一條就是别優化了!!!

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

“讓這代碼跑得快一點!!”——我碰到的第一件代碼優化任務就是這麼開始的。那個項目是一個巨大的 SAP 雲平台應用程式,總共含有超過 3 萬行的代碼。

整個 App 加載資料的過程非常之慢,顯然使用者并不喜歡這種體驗。

然而,我必須承認,這個項目的代碼寫的挺不錯,資料庫調用很合适,隻在有需要的地方進行循環,模組化也實作的很到位。我花了兩天時間,絞盡腦汁地進行各種測試,審查代碼邏輯,但完全沒發現到底是什麼地方讓這個程式變得如此之慢。

就在第三天,在我窮盡了所有的辦法,最後一點理智也快要消失的時候,我終于發現了問題所在。

在其中的一個讀取頁面上,被塞了一個等待語句,程式到這裡就停上 20 秒。

這大約是原來調試這段代碼的程式員在排查的過程中插入的等待指令,結果在将代碼合并進生産環境的時候忘記把這行東西去掉了。而在生産代碼中,每次調用讀取的時候,這段等待指令都會被執行,這就進一步放大了産生的問題。

于是,我把這行代碼删掉了。好家夥,一切都正常了!

有人說,代碼優化是一把雙刃劍

優化你的軟體是一件好事,但這并不能保證它永遠都會有好結果。

如你是在錯誤的原因驅動下,或是通過錯誤的方法進行代碼優化,這種所謂的優化往往可能增加成本,減緩生産速度,甚至可能會讓軟體的品質下降。

此外,大多數時候,優化并不是沒有代價的,你必須做出謹慎的權衡。例如,提高速度可能會使你在資源利用方面付出代價,更高效地利用存儲則很容易減慢運作速度。你需要仔細考慮你在其他方面做出的權衡,這樣你的軟體才能夠實作它的主要目标。

也許你會問,那我該怎麼辦?

下面是一些值得你考慮的要點,遵循這些原則,可以讓你的代碼更具響應性,也能減少你給使用者的裝置以及它們連接配接到的資料庫帶來的額外壓力。

1. 不要進行優化

代碼優化的第一條原則就是,“不要”優化它。

這個程式是不是已經足夠好了?你要去了解這個程式将會被如何使用,知道它是在怎樣的環境下運作的,明白如果讓它運作的更快到底有沒有好處。在真正開始代碼優化之前,你必須要問自己這幾個問題。

沒錯,代碼優化所耗費的經曆和成本,隻有在這樣的情況下是有意義的:

這個軟體很重要

它運作的确實很慢

在保證代碼健壯、正确、清楚的情況下,它确實還有改進的餘地

一個程式,就算它運作得再快,如果無法得到正确的結果,那就毫無用處。有效的優化,給軟體帶來的好處應該總要比壞處多。但如果你的優化走錯了路,那往往還不如别動它。

是以,你要做的第一件事,應該是設定一個合理的優化目标:

你需要清楚地了解你要達成的目标是什麼,以及各種優化手段與這個目标之間的關系。

你需要明确而簡單地說明這個目标,簡單到就算技術了解能力最差的部門經理也能夠了解和複述它。

你需要在整個過程中堅持這些目标。

要開始這項工作,最好的辦法是,根據對目标的影響确定每項任務的優先順序。你要做的每一件事情,都必須是可計量的。不要相信直覺,它基本上總是把你引向非常糟糕的方向。

2. 使用一個分析器

在沒有經過分析之前,不要貿然調整任何東西。最常見的錯誤做法就是,花了一整天去重構優化一段代碼,結果在運作的時候發現,這段代碼平時根本用不到。

分析器能精确地測量出你的程式把時間都花在什麼步驟上了。有些分析器能列出每一個函數,包括它們被調用的次數,以及每次執行的時候耗時的占比等。

還有的分析器能列出每個指令的執行次數,被頻繁執行的那些指令,在總占用時間上的權重肯定更高,而完全沒被運作的那些指令,往往就是一些無用的代碼,或者沒有經過合适測試的代碼。

一個好的分析工具,最有用的地方就是能讓你發現軟體中的“熱點”,也就是消耗了最多運作時間的那些函數或者指令語句。基本上如果你發現了一個熱點,你也就發現了問題所在。

性能分析的最佳使用方法就是識别出“熱點”,然後盡可能地優化它們,接着再次測量,以檢視是不是有新的熱點冒了出來。性能分析的之前分享過很多,關注微信公衆号網際網路架構師可以查找閱讀。

3. 啟用編譯器優化

通常情況下,有種比較靠譜的優化方式,那就是打開編譯器提供的那些内置的優化選項。

編譯器優化通常會給你的程式帶來幾個百分點到兩倍的運作速度提升。但某些情況下,這也可能反而降低速度,是以你需要在最終傳遞之前仔細測量性能優化的結果。不過總的來說,現代的編譯器在這方面已經做的足夠好了,程式員基本上再也不需要像以前那樣,不停地對編譯參數做各種頻繁的小調整。

一些現代的編譯器還具備全局優化能力,可以分析你的整個程式,以獲得潛在的提升。如果你的系統中有這樣的編譯器,請一定要試試。它可能會把運作時間減少個幾秒鐘。

注意:編譯器的優化設定越激進,最終編譯出來的程式中出現不明 Bug 的可能性也越高。是以,強烈建議你在開啟編譯器的優化選項後,務必重新進行回歸測試,以避免出現一些奇怪的意外。

4. 調整代碼

隻有到這時,你才真正開始修改調整代碼。在此之前,你必須已經通過第二步的性能分析發現了“熱點”,并且試過使用編譯器進行優化——畢竟絕大多數這些問題能讓編譯器幫你解決,也避免了你把這些代碼弄得過于複雜。

那麼,一般來說,有幾種比較成熟的方法來處理這些“熱點”。再次提醒,你必須非常謹慎,確定在送出每個更改之前,對它産生的影響進行測量。

那麼,讓我們看看這幾個方法吧。

将常用的表達式計算歸集在一起

如果同一個非常消耗性能的計算在多個地方重複出現,最好能隻在一個地方進行計算,然後記住計算結果。除非必要,否則不要在循環中進行這樣的計算。

用簡單的計算代替消耗性能的算法

字元串處理對于任何一個程式來說,都算是非常常見的運算了。但如果你用錯誤的辦法去處理字元串,它們也有可能消耗大量的性能。類似的,在某些情況下,你可以用一系列移位操作來代替乘法運算。

但請務必注意,這種方式或許能帶來一些性能提升(其實并不一定),也有可能讓你寫出非常崎岖複雜的代碼。是以在重構的時候,你必須非常注意代碼可讀性,以免寫出無法維護的代碼。

消滅循環

循環,往往是開銷最大的行為,沒有之一。在允許的情況下(例如疊代數量不太多的時候),盡量避免使用循環。

緩存常用的值

緩存能有效地利用本地性——也就是程式(以及使用者)更傾向于重用最近的資料。你隻需要緩存最常用的字元或資料,就能大大提高程式的性能。

使用一種更低層次的語言重寫

警告:不到萬不得已,不要這樣玩。

更低層次的語言在利用硬體裝置性能方面往往更具效率(看看 Python 裡的内置函數是用 C 寫的就知道了),但要寫好這些東西,将會消耗更多的程式設計開發時間。

有時,通過用低層次的程式設計語言重寫關鍵代碼,能獲得較大的性能提升,但這是以降低可移植性為代價的,也會讓以後的維護變得非常困難。是以,請謹慎做出決定。

請記住:在優化工作中,做出選擇這件事占了90%的權重。值得花時間來決定你要做什麼,以及怎樣才能做的對。當然,這也正是程式設計的黑科技之處!

5. 在你的管理模型中加入代碼審查環節

這條是同時寫給開發者和管理者的。對于軟體工程的管理者,你必須確定代碼審查是項目開發過程的一部分;對于開發者,你應當将代碼審查作為最佳程式設計做法中的必備環節。

推薦看這篇:基于 Gitlab 的代碼審查。

低效的代碼不會對系統的日常運作造成太大影響。由于這個明顯的理由,我們往往會傾向于讓效率低下的代碼通過審查——因為它并沒有産生任何真正的傷害,不是嗎?這可不對。随着時間的推移,代碼效率将會越來越低下,并且導緻執行速度變慢,最終使用戶端的處理時間大大超過可以接受的範圍。

是的,引入正常代碼檢查,删除效率低下的代碼片段,或許會給你增加許多工作量。但從長遠來看,如果你把那些低效的代碼留在原地,未來你将不得不付出成倍的工作量,去檢查為什麼代碼的運作要花上這麼長的時間——那時的你一定會感激現在的自己。是以說,不要讓現在的偷懶成為你未來的痛苦。盡可能檢查并優化你的代碼效率。

一定要讓别人檢查你的代碼。理想的情況下,檢查者是你所欽佩的某個大佬,但基本上任何開發者都能互相檢查。不過,如果某人根本看不懂你的某些代碼,那可要非常警惕了——要麼是檢查者的水準問題,要麼就是你的代碼可讀性實在太爛了。

結語

最後,任何代碼的改進,都是從你自身開始的。在程式設計的世界裡,你不可能從第一遍就非常完美地寫出代碼。你總需要對代碼進行更改、修正錯誤,甚至有時代碼無論如何都無法按照你想要的方式工作。

這沒什麼問題,這完全就是成為一名程式員的必經之路。讓寫出幹淨的代碼,成為你的習慣吧。

正如極限程式設計的創始者,設計模式的先驅肯特·貝克(Kent Beck)指出的那樣:“我不是一個偉大的程式員,我隻是一個不錯的程式員,加上偉大的習慣。”

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/zhibo

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-06-07

本文作者: Ravi Shankar Rajan

本文來自:“

網際網路架構師 微信公衆号

”,了解相關資訊可以關注“

網際網路架構師