CSAPP 優化程式效能 二

2021-08-02 19:49:35 字數 2807 閱讀 7372

程式示例

為了說明乙個抽象程式是如何被系統地轉換成更有效的**的,我們使用基於如下所示的向量資料結構的執行示例

向量由兩個記憶體塊表示,頭部和資料陣列,頭部宣告結構如下, data_t代表基本資料型別:

typedef struct vec_rec, *vec_ptr;
生成向量,訪問向量元素,確定向量長度的基本過程

vec_ptr new_vec(long len)

}result->data = data;

return result;

}

int get_vec_element(vec_ptr v, long index, data_t *dest)
long vec_length(vec_ptr v)

作為優化示例考慮下面**,將乙個向量中所有元素合併成乙個值

通過不同的巨集定義來執行不同的運算

#define ident 0

#define op +

#define ident 0

#define op *

void combine1(vec_ptr v, data_t *dest)

}

combine1 的cpe度量值

combine1呼叫函式vec_length作為for迴圈的測試條件,每次迴圈迭代都必須對測試條件求值,另一方面,向量的長度並不會隨著迴圈的進行而改變,我們對程式進行修改

改進迴圈測試效率,通過吧vec_length()移出迴圈,我們不需要每次迭代都執行這個函式

void combine2(vec_ptr v, data_t *dest)

}

combine2的cpe度量值

對於這種改變在**呼叫函式,呼叫函式多少次的變換,編譯器會非常的小心謹慎,程式設計師必須幫助編譯器顯示地完成**移動。

減少過程呼叫

過程呼叫會帶來開銷,妨礙大多數形式的程式優化

從combine2的**可以看出,每次迴圈迭代都呼叫get_vec_element來獲取下乙個向量元素,對每個向量都進行邊界檢查,很明顯會造成低效率,在處理任意陣列訪問時,邊界檢查很有用,但是對combine2的**簡單分析表明,所有的引用都是合法的。

為我們的資料型別增加函式get_vec_start來獲取陣列起始位址來獲取元素

data_t *get_vec_start(vec_ptr v)

void combine3(vec_ptr v, data_t *dest)

}

檢視combine3的cpe度量圖

驚奇的是效能沒有提公升,整數求和還略有下降,顯然內迴圈中的其他操作形成了瓶頸,限制效能超過get_vec_element,後文我們還會再回到這個函式,看看為什麼combine2中反覆的邊界檢查不會讓效能更差。

消除不必要的記憶體引用

再次我們給出資料型別為double(8位元組)

檢查編譯內迴圈產生的**

//dest in %rbx, data+i in %rdx, data+length in %rax

.l17:

vmovsd (%rbx), %xmm0

vmulsd (%rdx), %xmm0, %xmm0

vmovsd %xmm0, (%rbx)

addq $8, %rdx

compq %rax, %rdx

jne .l17

每次迭代,累計變數的值都要從記憶體讀出再寫回記憶體,這樣的讀寫很浪費,因為每次迭代開始時從dest讀出的值就是上次迭代最後寫入的值

所以我們隊程式再次進行改進,引入臨時變數acc(累積器accumulator),再迴圈中用來累計計算出的值,只有迴圈迭代結束才存放入dest,將combine3每次迭代的2次讀,1次寫減少到只需要1次讀。

void combine4(vec_ptr v, data_t *dest)

x86-64彙編**

// acc in %xmm0, data+i in %rdx, data+length in %rax

.l25:

vmulsd (%rdx), %xmm0, %xmmo

addq $8, %rdx

cmpq %rax, %rdx

jne .l25

檢視combine4的cpe度量值

可能有人會認為,編譯器會自動將combine3中的**轉換為combine4中的**所做的那樣,但是實際上由於記憶體的別名使用(兩個指標指向同一塊記憶體的情況),兩個函式會有不同的行為。

優化程式效能(CSAPP)

一 程式優化綜述 1 高效程式的特點 1 適當的演算法和資料結構。方法和資料的組織形式無疑是最關鍵的,是優化的基礎 2 能夠被編譯器轉化成高效的可執行 需要深入了解使用的編譯器的優化方法,和常見的優化策略 3 運用現代並行程式設計技術。多核以及硬體支援提供更大的加速可能,例如gpu 2 優化程式的一...

CSAPP 優化程式效能

1.選擇合適的演算法和資料結構。2.編寫出編譯器能夠有效優化以轉換為高效可執行的源 3.平行計算。當然重點還是第乙個,良好的演算法和資料結構大大減小了程式的時間複雜度。編譯器可以對程式進行不同程式的優化,在終端中,編譯時新增命令列選項 o1,o2等等可以進行不同級別的優化,這樣雖然提高了程式的效能,...

讀CSAPP 2 程式效能優化

合適的資料結構與演算法 編寫出編譯器能夠有效優化以轉換成高效可執行 的原始碼。將運算量特別大的計算,可以分成多部分,這些部分可以在多核多處理器的某種組合上並行處理 本篇主要以第二點進行討論,編譯器在優化的時候只會做最壞打算,做各種假設。為了保證程式的準確性,捨棄效能優化。void twiddle1 ...