2011-06-05 33 views
6

有人可以請我提供一些建議,說明如何通過多線程減少循環的運行時間?假設我也有兩個叫做'a'和'b'的向量。向量的並行總和

for (int j = 0; j < 8000; j++){ 
    // Perform an operation and store in the vector 'a' 
    // Add 'a' to 'b' coefficient wise 
} 

這個for循環在我的程序中執行了很多次。上面的for循環中的兩個操作已經過優化,但它們只能在一個內核上運行。但是,我有16個內核可用,並希望使用它們。

我試着修改循環如下。我沒有矢量'a',而是有16個矢量,並且假設第i個矢量被稱爲[i]。我的for循環,現在看起來像

for (int j = 0; j < 500; j++){ 
    for (int i = 0; i < 16; i++){ 
     // Perform an operation and store in the vector 'a[i]' 
    } 
    for (int i = 0; i < 16; i++){ 
     // Add 'a[i]' to 'b' coefficient wise 
    } 

} 

我使用OpenMP的每個的for循環中通過添加「OMP的#pragma爲平行」每個內部循環之前。我所有的處理器都在使用中,但運行時間僅顯着增加。有沒有人有關於如何減少此循環的運行時間的任何建議?先謝謝你。

+0

你是否分析過你的代碼,看看瓶頸在哪裏? – GWW 2011-06-05 06:04:39

+0

這可能是因爲可能在優化代碼之後,代碼不能被分解成更小的代碼片段,如果你的原始代碼只做'a [i] + = b [i]',那麼你可以在那之前添加該編譯標籤對於。它會提高你的表現,如你所想。 – Ali1S232 2011-06-05 06:06:41

+0

如果你的循環體非常微不足道,那麼你很可能受到內存帶寬的限制,而更多的內核將無濟於事(因爲內存帶寬已經飽和)。重新安排在更高層次上,以找到循環內部需要做的更多工作,或者獲得具有更快內存的機器。 – Nemo 2011-06-05 06:10:34

回答

5

omp爲插入編譯標籤的程序創建線程,所以它爲內部標籤創建線程,但問題是創建了16個線程,每個線程都執行1個操作,然後使用您的方法銷燬所有線程。創建和銷燬線程需要很長時間,所以您使用的方法會增加整個流程的整個時間,儘管它使用全部16個內核。你不必在8000循環之前創建內部函數,只需要在你的8000循環之前添加#pragma omp parallel for標籤就可以了,這樣就可以實現單獨的值之間的分隔值,所以你創建第二個循環的過程就是omp的工作。方式OMP是創建線程只有一次,然後再處理500號期運用每個線程和後(使用499更少的線程創建和銷燬)結束所有的人都

+0

這是我第一次使用OpenMp,但我在主要方法中使用'omp_set_num_threads(16)'。它不是作爲一個線程池而且不會銷燬任何線程? – 2011-06-05 06:21:37

+0

線程就像函數一樣,不能添加或刪除列表中的任何操作。只是將線程創建函數看作是獲取指針的函數作爲輸入。 'omp_set_num_threads'順便說一下是可選的,如果你不把它放在那裏,omp本身會選擇它應該創建多少個線程來優化你的代碼,設置只是強制omp使用你指定的使用量。 – Ali1S232 2011-06-05 06:33:00

+0

[omp]的wiki頁面(http://en.wikipedia.org/wiki/OpenMP)包含了幾乎所有需要的數據來創建一個簡單的omp程序,只需在任何調試之前閱讀它即可。 – Ali1S232 2011-06-05 06:38:51

0

Does anyone have any suggestions on how I can decrease the runtime of this loop?

for (int j = 0; j < 500; j++){ // outer loop 
    for (int i = 0; i < 16; i++){ // inner loop 

總想設法讓外循環迭代次數小於內循環。這將多次從內循環初始化中節省。在上面的代碼內部循環i = 0;初始化爲500次。現在,

for (int i = 0; j < 16; i++){ // outer loop 
    for (int j = 0; j < 500; j++){ // inner loop 

現在,內循環j = 0;只初始化了16次! 通過相應地修改您的代碼進行嘗試,如果它產生任何影響。

+3

在現代CPU上,這個建議正好倒退了。一系列_small_內部循環更有可能對L1緩存中的數據進行操作,這比初始化循環計數器的開銷要重要100-1000倍。 – Nemo 2011-06-05 06:29:04

+0

@Nemo,謝謝,我不知道。但是,在這裏,您可能是指* small * loop而不是* inner * loop是正確的?這也將取決於緩存大小。如果緩存大小真的能夠容納*更大的*循環,那麼邏輯關於仍然維持。 – iammilind 2011-06-05 06:32:27

+0

是的。關鍵是緩存友好,現在一個典型的L1緩存大概是32K。所以你要確保你的內部循環接觸的數據比那更少...... – Nemo 2011-06-05 06:36:59

3

其實,我會把這些評論放在答案中。

瑣碎操作的分叉線程只會增加開銷。

首先,確保您的編譯器使用向量指令來實現您的循環。 (如果它不知道如何做到這一點,你可能需要自己編寫向量指令;嘗試搜索「SSE內部函數」,但對於這種簡單的向量加法,自動向量化應該是可能的。)

假設你的編譯器是一個比較現代的GCC,與調用它:

gcc -O3 -march=native ... 

添加-ftree-vectorizer-verbose=2找出它是否自動向量化的循環,以及爲什麼。

如果您已經在使用矢量指令,那麼可能會導致內存帶寬飽和。現代CPU內核速度相當快......如果是這樣,您需要在更高級別進行重構,以便在每次迭代循環中獲得更多操作,找到適合L1緩存內的塊的大量操作。

+0

感謝您的評論。我確實啓用了SSE2,並且我正在使用微軟的Visual Studio 2010 IDE編譯器。我會考慮這一點,看看我是否可以重組其他任何東西。 – 2011-06-05 06:28:52

+0

我建議用-S運行以查看程序集並確保它使用SSE2指令。 – Nemo 2011-06-05 06:30:08