2013-03-10 63 views
2

使用OpenMP時,此代碼較慢。沒有OpenMP,我會獲得大約10秒。隨着OpenMP我大約40多歲。發生什麼事?非常感謝你的朋友!OpenMP的代碼如何可以並行化?

for (i=2;i<(nnoib-2);++i){ 
    #pragma omp parallel for 
    for (j=2; j<(nnojb-2); ++j) { 
     C[i][j]= absi[i]*absj[j]* 
       (2.0f*B[i][j] + absi[i]*absj[j]* 
       (VEL[i][j]*VEL[i][j]*fat* 
       (16.0f*(B[i][j-1]+B[i][j+1]+B[i-1][j]+B[i+1][j]) 
       -1.0f*(B[i][j-2]+B[i][j+2]+B[i-2][j]+B[i+2][j]) 
       -60.0f*B[i][j] 
       )-A[i][j])); 
     c2 = (abs(C[i][j]) > Amax[i][j]); 
     if (c2) { 
      Amax[i][j] = abs(C[i][j]); 
      Ttra[i][j] = t; 
     } 
    } 
} 
+0

'c2'聲明在哪裏? – Mysticial 2013-03-10 02:54:27

+0

c2是一個全局變量。 c2是int。謝謝! – 2013-03-10 03:38:14

+1

然後讓它成爲本地的,因爲會有'c2'的競爭條件/依賴。 – Mysticial 2013-03-10 03:39:06

回答

3

僅僅因爲您使用OpenMP並不意味着您的程序運行得更快。一對夫婦的事情可以發生在這裏:

  1. 有關聯的產卵每個線程有代價的,如果你生成一個線程做一個小的計算量,線程的產卵本身將花費更多的時間比計算。

  2. 默認情況下,OpenMP將產生CPU支持的最大線程數。對於每個核心支持2個或更多線程的CPU,線程將爭奪每個核心的資源。使用omp_get_num_threads()你可以看到有多少線程默認會產生。我建議您嘗試使用omp_set_num_threads()來運行代碼的一半。

你確認結果都是一樣的使用和不使用OpenMP的?似乎有變量j和c2的依賴關係。您應聲明它們對每個線程專用:

#pragma omp parallel for private(j,c2) 

我想添加另一件事:在嘗試任何並行化之前,你應該確保該代碼已經進行了優化。

取決於你的編譯器,編譯器標誌和指令的複雜性,編譯器可能會或可能不會優化代碼:

// avoid calculation nnoib-2 every iteration 
int t_nnoib = nnoib - 2; 
for (i=2; i< t_nnoib; ++i){ 
    // avoid calculation nnojb-2 every iteration 
    int t_nnojb = nnojb - 2; 
    // avoid loading absi[i] every iteration 
    int t_absi = absi[i]; 
    for (j=2; j< t_nnojb; ++j) { 
     C[i][j]= t_absi * absj[j] * 
      (2.0f*B[i][j] + t_absi * absj[j] * 
      (VEL[i][j] * VEL[i][j] * fat * 
      (16.0f * (B[i][j-1] + B[i][j+1] + B[i-1][j] + B[i+1][j]) 
       -1.0f * (B[i][j-2] + B[i][j+2] + B[i-2][j] + B[i+2][j]) 
       -60.0f * B[i][j] 
      ) - A[i][j])); 

     // c2 is a useless variable 
     if (abs(C[i][j]) > Amax[i][j]) { 
      Amax[i][j] = abs(C[i][j]); 
      Ttra[i][j] = t; 
     } 
    } 
} 

它可能看起來並不多,但它可以產生巨大影響您的碼。編譯器會嘗試將局部變量放入寄存器(訪問時間更快)。請記住,由於您的寄存器數量有限,因此無法無限制地應用此技術,濫用此操作會導致代碼遭受寄存器溢出。

對於數組absi,您將避免系統在執行j循環期間將該數組的一部分保留在緩存中。這種技術的一般想法是將外部循環移到不依賴內部循環變量的任何數組訪問。

+0

好的!我將是修改。我等於1000,j也等於1000.非常感謝你的朋友! – 2013-03-10 03:36:14

2

除了由克里斯蒂亞諾提到的成本,你的選擇並行在j循環而不是在i循環造成的假共享三個陣列的危險被分配,C, Amax, Ttra。實際上,當一個線程寫入其中一個數組的元素時,同一緩存行上的連續元素也將被加載到該內核的緩存中。當另一個內核接着將自己的值寫入不同的條目時,它將不得不從另一個緩存中拉出線,多個內核可能會玩'拔河'。

解決方案是通過i而不是j上的內循環並行化外循環。方便的是,這也大大降低了克里斯蒂亞諾的回答中提到的成本,因爲產卵和工作分配只會發生一次,而不是通過循環的每次迭代。您仍然需要對jc2進行私有化,或者在後續的if中簡單地將c2的值內聯,並消除該變量(如註釋中所述)。爲了提高效率,使用本地聲明而不是j將意味着不必訪問線程專用變量。

就像一個(相當重要的)檢查一樣,這個循環嵌套實際上是你測試過的佔用大部分時間的程序部分?添加OpenMP編譯指令將時間從10多歲改爲40多歲?

+0

請注意,對於本答案的第2段,OpenMP實現通常不會在執行期間動態創建和銷燬線程,而是在啓動時創建線程並在最終完成時銷燬線程。這符合OpenMP最初的用途,它不是一個通用的線程系統。 – 2013-03-10 11:09:16

+0

良好的實現可以在語義上與程序員所要求的語義相匹配。即便如此,在每次迭代中將工作從主線程傳遞給工作人員仍然存在開銷(可能不可忽略,具體取決於調度策略)。 – Novelocrat 2013-03-11 15:32:09