1. 重中之重 - 演算法優化:
程式效能優化最顯著的優化方法是演算法上的優化,演算法的優化對效能的提公升往往是乙個數量級的,例如排序,冒泡的時間複雜度為o(n^2),而快速排序的時間複雜度為o(nlog(n)),這種效能的提供是非常明顯的。
2. 消除冗餘的迴圈:
我們先看一下for迴圈生成的彙編**for (int n = 4, i = 0; i < n; ++i){}
15 movl $4, -4(%ebp) // n = 4
16 movl $0, -8(%ebp) // i = 0
17 jmp .l2
18 .l3:
19 addl $1, -8(%ebp) // ++i
20 .l2:
21 movl -8(%ebp), %eax
22 cmpl -4(%ebp), %eax // i - n
23 setl %al
24 testb %al, %al
25 jne .l3
從上面的彙編**中可以看出每次執行迴圈的時候都要執行19到25行這6彙編指令,因此減少迴圈可以提高程式效能。
例如上面的**可以寫成如下開形式:
for(int n = 4, i = 0; i < n; i += 2)
3. 減少過程函式呼叫:
我們知道程式中函式呼叫的開銷是非常大的,我們看一下簡單的函式呼叫的開銷:
int add ( int a , int b)
其彙編**如下:
5 _z3addii:
6 .lfb0:
7 .cfi_startproc
8 .cfi_personality 0x0,__gxx_personality_v0
9 pushl %ebp
10 .cfi_def_cfa_offset 8
11 movl %esp, %ebp
12 .cfi_offset 5, -8
13 .cfi_def_cfa_register 5
14 movl 12(%ebp), %eax //取b
15 addl %eax, 8(%ebp) // a += b
16 popl %ebp
17 ret
從彙編**中可以看出函式呼叫的開銷是非常大的。
假設有乙個程式
const
int num = 100;
int getnum()
for (int i = 0; i < getnum(); ++i)
最優化的寫法是
int inum = getnum();
for (int i = 0; i < inum; ++i)
當然這種寫法的前提是getnum()的數值在執行for的時候是沒有變化的。
4. 消除不必要的儲存器引用 :
void add(int * array , int len , int * res)
}其彙編**(去掉for)
19 movl 12(%ebp), %ebx // %ebx,存len的位址
20 movl 16(%ebp), %edx // %edx, 存*res的位址
21 movl $0, (%edx) // 寫mem
22 testl %ebx, %ebx
23 jle .l4
24 movl $0, %eax // i = 0;
25 .l3:
26 movl (%esi,%eax,4), %ecx // 取array[i]
27 addl %ecx, (%edx) // +=,寫mem
28 addl $1, %eax //
29 cmpl %ebx, %eax
30 jne .l3
從上面的彙編**分析中可以看出,每次for迴圈都進行了寫記憶體。我們知道讀寫記憶體是非常耗時的,因此可以對其進行優化。
void add(int * array , int len , int * res)
*res = sum;
}//彙編
53 movl 12(%ebp), %ecx
54 movl $0, %eax
55 movl $0, %edx // sum = 0
56 testl %ecx, %ecx
57 jle .l8
58 .l11:
59 addl (%ebx,%eax,4), %edx // sum += array[i];, 只寫暫存器
60 addl $1, %eax
61 cmpl %ecx, %eax
62 jne .l11
63 .l8:
64 movl 16(%ebp), %eax
優化後的**在迴圈中少了一次暫存器的讀寫。
注意上面的**是經過了-o選項優化的。
5. 增強流水線處理能力。
現在的處理器,每個核心在會有多個執行單元,相互之間採用流水線的執行方式,如果前後兩個操作沒有依賴關係,可以並行執行。
考慮乙個求和的for迴圈
int array[n]; 假設n為偶數
int sum = 0
for (int i = 0; i < n; ++i)
將其流水線化可以表示如下:
int sum1 = 0;
int sum2 = 0;
for (int i = 0; i < n; i += 2)
sum1 += sum2;
由於 sum1 += array[i];
sum2 += array[i + 1];
是沒有相互依賴的,因此可以流水線執行。
6. cache對程式效能的影響
參看本人關於「矩陣乘積的並行」演算法的討論
7. 效能除錯工具:
gprof .
$ g++ program.cpp -o program -pg
$ ./program
$ gprof program;
注意一定要帶-pg引數
優化程式效能
編寫高效程式需要兩個活動 第一,我們必須選擇一組最好的演算法和資料結構 第二,我們必須編寫出編譯器能夠有效優化以轉換成高效可執行 的源 這裡,我們主要講述後者。首先,我們討論一下為什麼要編寫高效程式。不難想象,如果本來要用 天執行完的程式,經過優化只需要 天就可執行完,這是一件多麼令人振奮的 事啊。...
優化程式效能
l 消除迴圈的低效率 n 對於迴圈中的過程呼叫盡量移出迴圈外,例如 nfor i 0 i strlen s i strlen 函式為線性增長 在字串長度很大時 很消耗系統資源 n 減少不必要的儲存器引用,將儲存器引用儲存在臨時變數中.l 處理器優化 即充分利用儲存器流水線操作的吞吐量 n 迴圈展開,...
優化程式效能
研究彙編 是理解編譯器以及產生的 會如何執行的最有效的手段之一。編譯器優化 的限制 1 程式設計中存在 儲存器別名使用 的問題。編譯器必須假設不同的指標可能指向儲存器中相同的位置。2 函式呼叫 簡略了。具體看書 基本的編碼原則 效能大幅度提公升 優化程式效能的一些方法 1 將除錯完的程式完成編譯器級...