程序性能優化

開場白:最近公司招人,接觸了一批形形色色的工程師,但感受絕大多數人基礎都不好,在某次TL的討論以後,就想到了寫一個《面試寶典》系列。nginx

卷首語:這個《面試寶典》名字是我一向的標題黨風格,其實在內容上都是很簡單、很基礎的——都是那種「不知道這些就別出來混」的知識點。因此,高手/牛人能夠到此打住了——端咖啡——送客~~~面試

做/譯序:此文可能會持續更新直到補充得比較完整爲止,有什麼要補充的歡迎留言
算法

基礎原則之萬能利器:Profiling

如何測量距離?——直尺/遊標卡尺/捲尺/etc
如何測量電壓?——電壓表/萬用表
如何測量時間?——鐘錶/秒錶
如何測量溫度?——溫度計
如何測量質量?——天平/檯秤/etc
如何測量程序性能?——Profiler !緩存

是的,Profiler就是這麼一件又基礎又重要的工具(小標題裏的「萬能」是標題黨了)。要回答「程序爲何慢/什麼地方慢/到底有多慢」等問題,離不開Profiler,就像測量長度離不開尺子同樣。 (嗯,那位要測地月距離的,別擡槓了,請移步回你的實驗室吧,我知道你是rocket scientist,高科技呢:)性能優化

可使用軟件Profiler,也可使用硬件來作Profiling(DSO/Logic Analyzer/Hybrid DSO/Timer),在什麼都沒有的狀況下,至少也能夠輸出Log或者用其它方式來達到Profiling的目的。
通常來講,測量工具對被測目標應該沒有影響(或者極小),可是軟件的Profiler卻對被測目標有必定程度的影響——以此爲代價,換來的是方便和廉價(免費)。服務器

在系統外部對性能進行測試,也是一種測量性能的方式,只不過它所測量的粒度很大,不能用於分析源碼,只能用於比較不一樣源碼的優劣。數據結構

如何進行性能優化?

基本原則

原則:使用Profiler來找出最影響性能的那些程序,重點優化它們
分類:基本知識
提示:時刻記心間併發

原則:不管嘗試了何種優化,都要使用profiler來測量這種優化
分類:基本知識
提示:時刻記心間異步

原則:二八原則
分類:基本知識
提示:運行中的軟件有一種典型狀況:20%的程序佔了80%的運行時間,優化的重點是這20%的程序(20%和80%爲約數)。應當使用Profiler來找出它們,改進它們並繼續用Profiler測量改進的效果——在沒有明顯的性能熱點的時候,就要考慮換一種思路進行優化了:好比使用「硬件優化」(參見下文)。在切換了設計和體系結構後,以前作的profiling也有可能會失效,要從新進行測量,每一種判斷和決策必須獲得證據的支持。分佈式

硬件優化

原則:使用更快的設備/通信協議
分類:硬件優化
提示:有時候須要嘗試跳出軟件優化的框框來想問題
範例:使用SSD代替機械硬盤,在IO速率上你能提升2-5倍(在無RAID的狀況下),在尋道時間上你能提升4個數量級(WOW!)

原則:對硬件友好
分類:硬件優化
範例:利用SIMD指令(如視頻編碼器);對Cache友好(數據Cache和指令Cache);對流水線友好(好比threaded code,削減分枝預測失誤。注意這裏的thread不是「線程」,是「線索化」);使用硬盤的外圈以達到更大的速度;

原則:利用硬件加速
分類:硬件優化
範例:使用D3D/OpenGL,使用GPU計算(如FFT),使用DMA不使用PIO,使用ASIC代替CPU進行專用計算(如常見的視頻編解碼專用芯片,還有加解密芯片),使用DSP對高負載數據進行處理(或預處理)

設計優化

原則:使用分佈式計算
分類:設計優化
範例:使用Hadoop;使用Memcached

原則:改進業務邏輯
分類:設計優化
備註:暫不提供例子

原則:批量操做
分類:設計優化
備註:實際上是業務邏輯優化的一個特例

原則:對輸入進行緩存
分類:設計優化,緩存
範例:依靠CPU Cache/陣列Cache/磁盤Cache/磁盤/操做系統Cache/程序管理的Cache/專用Cache服務器等,對輸入進行緩存,以加快速度

原則:對中間結果進行緩存
分類:設計優化,緩存
範例:預裝載/索引/suffix tree

原則:對輸出進行緩存
分類:設計優化,緩存
範例:使用buffer批量輸出(匹配軟件計算速度和設備的速度)

原則:空間換時間
分類:設計優化
範例:在DB中使用denormalized data
備註:這是一個比較通用的原則,Cache也是空間換時間。

原則:使用內存池
分類:設計優化,內存

原則:使用更好/更合適的GC算法
分類:設計優化,內存

原則:控制內存交換
分類:設計優化,內存

原則:吸取掉沒必要要的界面更新
分類:設計優化,GUI

原則:只重繪更新的區域
分類:設計優化,GUI

原則:對可視化的結果進行緩存
分類:設計優化,GUI
備註:中間結果緩存的一個特例

原則:parallelism–利用多CPU/多核心
分類:設計優化,並行

原則:利用併發(concurrency)
分類:設計優化,並行
提示:在單CPU上跑併發也能提升性能
範例:線程池

原則:選取合適的鎖類型
分類:設計優化,並行,鎖
範例:使用讀寫鎖

原則:使用消息
分類:設計優化,並行,鎖
備註:能夠減小共享數據和鎖

原則:使用異步模型
分類:設計優化,並行,鎖
範例:使用epoll/kqueue等;nginx

原則:消除鎖
分類:設計優化,並行,鎖
提示:試用無鎖的算法/數據結構/算法

原則:使用FP Paradigm
分類:設計優化,並行
提示:彪悍的FP不須要提示

算法優化

(其實算法每每是包含在設計中的)

原則:使用更優的算法,減少算法的階
分類:算法優化
範例:使用BM/Sunday算法代替BF算法

原則:減少算法的常量(BigOO)
分類:算法優化
範例:使用sentry來編寫double link list

原則:處理好Big Omega和BigO,使算法在「最壞的時候也不要太壞」
分類:算法優化
範例:quick sort,避免O(N*N)的狀況

原則:使用更優的數據結構
分類:算法優化
範例:AVL Tree–>Hash Table–>Ternary Search Tree
備註:其實有些重複,主要是爲了區分狹義的「算法」和「數據結構」

原則:尋找並行化算法
分類:算法優化
備註:和前面的parallelism有些重複,這個更多的是指可並行的算法而不是其它意義上的並行

小技巧

原則:編譯器優化
分類:小技巧
範例:使用Intel的編譯器,使用Intel的性能庫IPP
備註:注意Proebsting定律

原則:樹遞歸–>尾遞歸
分類:小技巧
備註:在許多語言中,尾遞歸是不須要棧的,自動轉換成迭代了(例如在erlang中用尾遞歸實現無限循環)

原則:少用小技巧,以避免妨礙大粒度上的性能優化
分類:小技巧
提示:主要是指那些影響並行計算的小技巧,或者那些沒有使用profiler進行測量就草率進行的盲目優化

原則:遠離「神話」
分類:小技巧
提示:有些流傳的關於優化的神化不足信,「是騾子是馬拉出來騮騮」,一切在profiler下見真章
範例:好比「大塊內存分配很是慢」的神話(甚至有過一我的估計數量級的時候說:分配10M內存至少要1秒鐘),好比「虛函數致使效率低」的神話,好比「解釋運行比編譯慢」的神話,等等

原則:使用Lazy Evaluation
分類:小技巧

原則:避免大對象複製
分類:小技巧
範例:RVO/RVal Ref

原則:預譯碼/編譯成本地代碼
分類:小技巧,解釋器和VM
範例:PYC/HipHop

原則:混合使用各有優點的語言
分類:小技巧
範例:C裏使用匯編;Python裏使用C模塊;

原則:使用二進制優化器
分類:小技巧
範例:JIT就是最出名的一種二進制優化;Win32SDK的BitBlt()函數也是;

……

重溫:不管嘗試了何種優化,都要使用profiler來測量這種優化分類:紅寶書提示:時刻記心間

相關文章
相關標籤/搜索