2012-12-27 41 views
2

我正在嘗試並行化基於粒子模擬的代碼,並且遇到基於OpenMP的方法性能較差的問題。我的意思是:用於粒子模擬的並行化OpenMP代碼的性能不佳

  • 使用Linux工具top顯示CPU使用率,運行CPU的OpenMP線程的平均使用率爲50%。
  • 隨着線程數量的增加,加速收斂到約1.6倍。收斂速度非常快,即使用2個線程可以達到1.5的加速。

以下僞碼說明了所有並行區域的基本模板。注意,在單個時間步驟中,正在執行以下所示方式的5個平行區域。基本上,作用在粒子上的力是相鄰粒子j < NN(i)的幾個場特性的函數。

omp_set_num_threads(ncpu); 

#pragma omp parallel shared(quite_a_large_amount_of_readonly_data, force) 
{ 
    int i,j,N,NN; 

    #pragma omp for 
    for(i=0; i<N; i++){    // Looping over all particles 
     for (j=0; j<NN(i); j++){  // Nested loop over all neighbors of i 
      // No communtions between threads, atomic regions, 
      // barriers whatsoever. 
      force[i] += function(j); 
     } 
    } 
} 

我想弄清楚觀察到的瓶頸的原因。我的天真初步猜測爲一個解釋:

如上所述,線程之間共享大量的內存用於只讀訪問。不同的線程很可能會嘗試同時讀取相同的內存位置。這是否造成瓶頸?我應該讓OpenMP分配私人副本嗎?

+0

這是*大量的數據*預先存儲,或者你從過程中的文件中讀取它? I/O將永遠在那裏摧毀人們的期望\ =正如您所說的,多次訪問同一個空間可能會導致抖動,因此設置一些訪問策略將會很好 – Rubens

+0

謝謝。我不需要從文件中讀取它。它們在運行時生成並存儲在物理RAM中。 –

+0

NN(i)是如何均勻分佈的?負載不平衡可能是一個問題嗎?您可以嘗試不同的時間表以供循環查看。否則,您需要使用分析器來查明您的時間花在哪裏;我非常喜歡[scalasca](http://www.scalasca.org)來解決OpenMP性能問題。 –

回答

2

N多大,以及NN(i)的密度如何?

你說什麼都沒有共享,但force[i]可能在force[i+1]的同一緩存行內。這就是所謂的false sharing,可能是非常有害的。 OpenMP應該將所有東西加在一起以彌補這一點,所以如果足夠大的話,我認爲這不會是你的問題。

如果NN(i)不是CPU密集型的,那麼你可能會遇到一個簡單的內存瓶頸問題 - 在這種情況下拋出更多內核並不能解決任何問題。

+0

謝謝。 N在10^7左右。 NN(i)約爲10^2,但計算量很大。我需要挖掘虛假分享。從來沒有這樣做過。 –

+0

我完全沒有想法,然後:)。你需要顯示'NN(i)'和'function(j)'的實現以獲得更好的答案。 –

+0

花了一些時間在這個話題上後,我覺得值得一試。將「強制」聲明爲私有並將其添加到原子區域內平行區域末端的全局力矢量中怎麼樣? –

1

假設force [i]是4或8字節數據的普通數組,您肯定有錯誤的分享,毫無疑問。

假設功能(j)的獨立計算,你可能需要做這樣的事情:

for(i=0; i<N; i+=STEP){    // Looping over all particles 
     for (j=0; j<NN(i); j+=STEP){  // Nested loop over all neighbors of i 
      // No communtions between threads, atomic regions, 
      // barriers whatsoever. 
     calc_next(i, j); 
     } 
    } 


void calc_next(int i, int j) 
{ 
    int ii, jj; 
    for(ii = 0; ii < STEP; ii++) 
    { 
     for(jj = 0; jj < STEP; jj++) 
     { 
      force[i+ii] = function(j+jj); 
     } 
    } 
} 

這樣,你算算在一個線程一堆東西,並在一堆東西下一個線程,每個線程都足夠分開,你不會得到錯誤的分享。

如果你不能這樣做,嘗試以其他方式拆分它,導致每次計算更大的部分。

0

正如其他人所說,在force上的虛假分享可能是一個原因。嘗試在這種簡單的方式,

#pragma omp for 
for(i=0; i<N; i++){ 
    int sum = force[i]; 
    for (j=0; j<NN(i); j++){ 
     sum += function(j); 
    } 
    force[i] = sum; 
} 

從技術上講,它可能是force[i] = sum仍然作出虛假共享。但是,這是不太可能發生的,因爲其他線程將訪問force[i + N/omp_num_threads()*omp_thread_num()],這與force[i]相差很遠。

如果仍然可擴展性差,請嘗試使用諸如Intel Parallel Amplifier(或VTune)之類的分析器來查看每個線程需要多少內存帶寬。如果是這樣,請在計算機中放置更多的DRAM :)這將真正提升內存帶寬。