2012-12-27 30 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 :)这将真正提升内存带宽。