優化程式效能(3) 提高並行性

2022-10-11 12:09:10 字數 2238 閱讀 7199

在之前的學習中,程式的效能是受運算單元的延遲限制的。正如我們表明的,執行加法和乘法的功能單元是完全流水線化的,這意味著它們可以每個時鐘週期開始乙個新操作,並且有些操作可以被多個功能單元執行。硬體具有以更高速率執行乘法和加法的潛力,但是**不能利用這種能力,即使是使用迴圈展開也不能,這是因為我們將積累值放在乙個單獨的變數acc中,在前面的計算完成之前,都不能計算acc的新值(順序依賴)。雖然計算acc值的功能單元能夠每個時鐘週期開始乙個新操作,但是它只會每l(l是合併操作的延遲)個週期開始一條新操作。

為了打破這種順序相關,得到比延遲界限更好效能的方法我們可以考慮設定多個積累變數。

對於乙個可結合和可交換的合併運算來說,比如說整數加法或乘法,我們可以通過將一組合併運算分割成兩個或更多的部分,並在最後合併結果來提高效能。

/* 2 x 2 loop unrolling */

void combine6(vec_ptr v, data_t *dest)

/* finish any remaining elements */

for(; i < limit ; i++)

*dest = acc0 op acc1;}1

2345

6789

1011

1213

1415

1617

1819

2021

22上述**既使用了兩次迴圈展開,以使每次迭代合併更多的元素,也是用了兩路並行,將索引值為偶數的元素累積在變數acc0中,而索引值為奇數的元素累積在變數acc1中。我們將它稱為「2x2迴圈展開」。比較只做迴圈展開和既做迴圈展開同時也使用兩路並行這兩種方法,我們得到下面的效能:

我們看到所有情況都得到了改進,整數乘、浮點加、浮點乘改進了約2倍,而整數加也有所改進。最棒的是,我們打破了由延遲界限設下的限制。處理器不再需要延遲乙個加法或乘法操作以待前乙個操作完成。

實際上,程式正在利用功能單元的流水線能力,將利用率提高到兩倍。唯一的例外是整數加。我們已將cpe降低到1.0以下,但是還是有太多的迴圈開銷,而無法達到理論界限0.50。

通常,只有保持能夠執行該操作的所有功能單元的流水線都是滿的,程式才能達到這個操作的吞吐量界限。對延遲為l,容量為c的操作而言,這就要求迴圈展開因子k≥c·l。比如,浮點乘由c=2,l=5,迴圈展開因子就必須為k≥10.浮點加有c=1,l=3,則在k≥3時達到最大吞吐量。

現在來**另一種打破順序相關從而使效能提高到延遲界限之外的方法——重新結合變換。我們看到過做kx1迴圈展開的combine5 沒有改變合併向量元素形成和或者乘積中執行的操作。對**做很少的改動,我們可以從根本上改變合併執行的方式,也極大地提高程式的效能。下面給出乙個函式combine7,它與combine5的展開**的唯一區別在於內迴圈中元素合併的方式。在combine5中,合併使以下面這條語句來實現的:

acc = (acc op data[i]) op data[i+1];

而在combine7中,合併是以這條語句來實現的

acc = acc op (data[i] op data[i+1] );

差別僅僅在於兩個括號是如何放置的。我們稱之為重新結合變換,因為括號改變了向量元素與累計值acc的合併順序,產生了我們稱為「2x1a」的迴圈展開形式。

/* 2 x 1a loop unrolling */

void combine7(vec_ptr v,data_t *dest)

/* finish any remaining elements */

for(; i < length ;i++)

*dest = acc;}1

2345

6789

1011

1213

1415

1617

1819

再來測試cpe時,我們得到令人吃驚的結果:

整數加的效能幾乎與使用kx1展開的版本(combine5)的效能相同,而其他三種情況則與使用並行累計變數的版本(combine6)相同,是kx1擴充套件的效能的兩倍。這已經突破了延遲界限造成的限制。

這是因為關鍵路徑上只有n/2個操作。每次迭代內的第乙個乘法都不需要等待前一次迭代的累計值就可以執行。因此,最小可能的cpe減少了兩倍。

注意:對於整數加法和乘法,這些運算是可結合的,這表示這種重新變換順序對結果沒有影響。對於浮點數情況,必須再次評估這種重新結合是否有可能嚴重影響結果。

總的來說,重新結合變換能夠減少關鍵路徑上操作的數量,通過更好地利用功能單元的流水線能力可以得到更好的效能。大多數編譯器不會嘗試對浮點運算做重新結合,因為這些運算不保證是可結合的。

--------------------- 

優化程式效能

編寫高效程式需要兩個活動 第一,我們必須選擇一組最好的演算法和資料結構 第二,我們必須編寫出編譯器能夠有效優化以轉換成高效可執行 的源 這裡,我們主要講述後者。首先,我們討論一下為什麼要編寫高效程式。不難想象,如果本來要用 天執行完的程式,經過優化只需要 天就可執行完,這是一件多麼令人振奮的 事啊。...

優化程式效能

l 消除迴圈的低效率 n 對於迴圈中的過程呼叫盡量移出迴圈外,例如 nfor i 0 i strlen s i strlen 函式為線性增長 在字串長度很大時 很消耗系統資源 n 減少不必要的儲存器引用,將儲存器引用儲存在臨時變數中.l 處理器優化 即充分利用儲存器流水線操作的吞吐量 n 迴圈展開,...

優化程式效能

研究彙編 是理解編譯器以及產生的 會如何執行的最有效的手段之一。編譯器優化 的限制 1 程式設計中存在 儲存器別名使用 的問題。編譯器必須假設不同的指標可能指向儲存器中相同的位置。2 函式呼叫 簡略了。具體看書 基本的編碼原則 效能大幅度提公升 優化程式效能的一些方法 1 將除錯完的程式完成編譯器級...