2013-02-08 64 views
0

我有兩個非常簡單的代碼段。我想它們平行如下:用MPI和OMP奇怪的減速

double sk = 0, ed = 0; 
     #pragma omp parallel shared(Z,Zo,U1,U2,U3) private(i) reduction(+: sk, ed) 
     { 
      #pragma omp for 
      for (i=0;i<imgDim;i++) 
      { 
       sk += (Z[i]-Zo[i])*(Z[i]-Zo[i]); 
       ed += U1[i]*U1[i] + U2[i]*U2[i] + U3[i]*U3[i]; 
      } 
     } 

////////////////////////////////// ////////////////////////////////////////////////// //

double rk = 0, epri = 0, ex = 0, ez = 0; 
      #pragma omp parallel shared(X,Z) private(i) reduction(+: rk, ex,ez) 
      { 
       #pragma omp for 
       for(i = 0; i<imgDim; i++) 
       { 
        rk += (X[0][i]-Z[i])*(X[0][i]-Z[i]) + (X[1][i]-Z[i])*(X[1][i]-Z[i]) + (X[2][i]-Z[i])*(X[2][i]-Z[i]);  
        ex += X[0][i]*X[0][i] + X[1][i]*X[1][i] + X[2][i]*X[2][i]; 
        ez += Z[i]*Z[i]; 
       } 
      } 

Z,Zo,U1,U2,U3,X都是大矩陣。 imgDim是400萬。加速並不如預期。在16核心機器上,這兩個小代碼的加速只有兩次。我不明白爲什麼OMP會出現這種行爲,因爲這兩個代碼只是增加了一些東西。這應該是OMP擅長的。

更奇怪的行爲是MPI慢下來時,我嘗試使用MPI並行這些代碼如下:

int startval = imgDim*pid/np; 
int endval = imgDim*(pid+1)/np-1; 
int ierr; 
double p_sum_sk = 0; 
double p_sum_ed = 0; 

for (i=startval;i<=endval;i++) 
{ 
    sk += (Z[i]-Zo[i])*(Z[i]-Zo[i]); 
    ed += U1[i]*U1[i] + U2[i]*U2[i] + U3[i]*U3[i]; 
} 

MPI_Reduce(&sk, &p_sum_sk, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); 
MPI_Reduce(&ed, &p_sum_ed, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); 
MPI_Bcast(&sk, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); 
MPI_Bcast(&ed, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); 

///////////////// ////////////////////////////////////////////////// //////////////////

int startval = imgDim*pid/np; 
int endval = imgDim*(pid+1)/np-1; 
double p_sum_rk = 0.; 
double p_sum_ex = 0.; 
double p_sum_ez = 0.; 


for(i = startval; i<=endval; i++) 
{ 
    rk = rk + (X[0][i]-Z[i])*(X[0][i]-Z[i]) + (X[1][i]-Z[i])*(X[1][i]-Z[i]) + (X[2][i]-Z[i])*(X[2][i]-Z[i]); 
    ex += X[0][i]*X[0][i] + X[1][i]*X[1][i] + X[2][i]*X[2][i]; 
    ez += Z[i]*Z[i]; 
} 

MPI_Reduce(&rk,&p_sum_rk,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD); 
MPI_Reduce(&ex,&p_sum_ex,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD); 
MPI_Reduce(&ez,&p_sum_ez,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD); 
MPI_Bcast(&rk,1,MPI_INT,0,MPI_COMM_WORLD); 
MPI_Bcast(&rk,1,MPI_INT,0,MPI_COMM_WORLD); 
MPI_Bcast(&epri,1,MPI_INT,0,MPI_COMM_WORLD); 

np是處理器的數量,pid是當前處理器的ID。在使用32個甚至64個處理器之後,它沒有顯示出任何加速。它甚至比順序代碼慢。我不懂爲什麼。這些代碼只是添加了一些東西。 OMP和MPI應該很擅長。任何人都可以幫我一把嗎?

回答

1

您的代碼是內存綁定的 - 您在每次迭代中加載大量數據並對其進行簡單(即快速)計算。如果imgDim 400萬,則即使Z每個元素,ZoU1U2U3是短至4個字節(例如,它們是floatint陣列),它們的總尺寸將是80 MIB和這將不適合在即使是雙插槽系統也是最後一級CPU高速緩存。如果這些數組的值爲double(事實上您減少爲double變量),事情會變得更糟,因爲它會增加雙倍的內存大小。另外,如果使用能夠矢量化代碼的體面編譯器(例如,icc默認爲GCC,則需要-ftree-vectorize),即使單個線程也能夠飽和CPU插槽的內存帶寬,然後運行更多比一條線索不會帶來任何好處。

我會說在16核心系統上觀察到的2x OpenMP加速來自於這個系統有兩個CPU插座並且是NUMA,即它在每個插座上都有一個單獨的存儲器控​​制器,因此當使用16個線程運行時,可以使用兩倍於單個插槽的內存帶寬。如果您僅使用兩個線程運行代碼,但可以用不同的方式綁定它們,則可以驗證這一點:同一個套接字上的每個內核有一個線程,或者每個內核有一個線程,但在不同的套接字上。在第一種情況下,應該不會加速,而在第二種情況下,加速應該是大約2倍。綁定到內核的線程還取決於實現 - 如果您碰巧使用了英特爾編譯器,那麼您可以參考GCC的GOMP_CPU_AFFINITYKMP_AFFINITY

這同樣適用於MPI情況。現在你有進程而不是線程,但是內存帶寬限制依然存在。情況更糟糕,因爲現在還增加了通信開銷,並且如果問題規模太小(這個比率取決於網絡互連 - 它會低於更快和更少的潛在互連,如QDR InfiniBand結構)。但是通過MPI,您可以訪問更多的CPU插槽,從而獲得更高的總內存帶寬。您可以通過每個套接字一個MPI進程啓動您的代碼,以便在系統中獲得最佳性能。在這種情況下,進程綁定(或用英特爾術語固定)也很重要。