CSAPP 5 - 優化程式效能

三千山人發表於2020-06-07

CSAPP 5 - 優化程式效能

1. 概述

  1. 首當其衝的,還是要編寫出好的演算法和資料結構,優化內部結構
  2. 其次才是編寫出能讓編譯器 易優化的,高效的可執行程式碼。這點在特定的機器上可能有著特定的不同的優化,但有一些基本的優化仍然是相同的。
    1. 消除不必要的工作,讓程式碼消耗時間在期望執行的任務上。包括消除或減少函式呼叫、條件測試、記憶體引用;同時熟悉處理器如何工作,利用反彙編知道它究竟如何執行操作,從而調整程式獲得最大的速度。
    2. 利用好處理器的指令級並行能力,同時執行多條指令

2. 利用好編譯器自身優化

gcc 就提供了不同優化級別的執行操作,利用 -O1 -O2 -O3 對程式進行不同級別的編譯器優化程式碼。gcc 也在不斷的更新迭代,編譯器自身也在越來越強大。

基本編碼規範:(避免限制優化的因素,產生更高效的程式碼)

消除連續的函式呼叫;避免不必要的記憶體引用

3. 消除迴圈的低效率,記憶體呼叫等

  • 展開迴圈,降低開銷
  • 實現指令級並行
  • 用功能性程式碼重寫條件操作,使得編譯採用條件傳送
for (i=0; i<len; i++)
    if (s[i] >= 'A' && s[i]<= 'Z')
        s[i] -= ('A' - 'a');

函式呼叫不斷使用,極其低效

4. 理解現代處理器

現代處理器採用分支預測的技術,處理器會猜測是否會選擇分支,同時還預測分支的目標地址。這就讓true/false 很痛苦,因為一旦預測失敗就會產生很大的代價。所以我們會盡可能的採用條件傳送(conditional move),而不是條件控制轉移。

條件控制轉移:等待程式碼的條件結果,選擇合適的路徑

for (i=0;i<n;i++)
    if(a[i]>b[i]){
        long t = a[i];
        a[i] = b[i];
        b[i]=t;
    }

條件傳送:先將結果執行,再根據結果選擇最終的結果值

for (i=0;i<n;i++)
    if(a[i]>b[i]){
        long min =a[i] < b[i] ? a[i] : b[i];
        long max =a[i] < b[i] ? b[i] : a[i];
        a[i] = min;
        b[i] = max;
    }

防止暫存器溢位

當並行度 p 超過可用暫存器的數量,編譯器就會溢位,將一些臨時變數放在記憶體中,會降低執行速度

5. 利用程式碼剖析程式,進行分析

Unix提供剖析程式GPROF可以進行程式碼剖析分析

程式碼剖析(Code profiling)
程式設計師在優化軟體效能時要注意應儘量優化軟體中被頻繁呼叫的部分,這樣才能對程式進行有效優化。使用真實的資料,精確的分析應用程式在時間上的花費的行為就成為_程式碼剖析_。現在幾乎所有的開發平臺都支援程式碼剖析,本文要介紹的是linux下針對c/c++的GNU的gprof程式碼剖析工具。

PS:gprof不只能對c/c++,還可對Pascal和Fortran 77進行程式碼剖析。

gprof
GNU gprof 是一款linux平臺上的程式分析軟體(unix也有prof)。藉助gprof可以獲得C/C++程式執行期間的統計資料,例如每個函式耗費的時間,函式被呼叫的次數以及各個函式相互之間的呼叫關係。gprof可以幫助我們找到程式執行的瓶頸,對佔據大量CPU時間的函式進行調優。

PS:gprof統計的只是使用者態CPU的佔用時間,不包括核心態的CPU時間。gprof對I/O瓶頸無能為力,耗時甚久的I/O操作很可能只佔據極少的CPU時間。

如何使用gprof
gprof的使用很簡單,遵循以下步驟即可:

參考文件:

https://sourceware.org/binutils/docs/gprof/index.html#Top

6. 小結

沒有一個編譯器能用好的演算法代替低效率的演算法,因此程式設計的內容才是仍然是程式設計師主要關心的。我們應當養成良好的程式碼風格,給後續的工作帶來便捷。

相關文章