并行化OpenMP代码进行粒子模拟的性能不佳

时间:2012-12-27 21:08:34

标签: c++ parallel-processing openmp

我正在尝试并行化基于粒子的模拟的代码,并且遇到基于OpenMP的方法的不良性能。我的意思是:

  • 使用Linux工具top显示CPU使用率,运行CPU的OpenMP线程平均使用率为50%。
  • 随着线程数量的增加,加速收敛到约1.6倍。收敛速度非常快,即使用2个线程达到1.5的速度。

以下伪代码说明了实现的所有并行区域的基本模板。 注意,在单个时间步骤期间,正在执行以下所示方式的5个平行区域。基本上,作用于粒子i < N的力是相邻粒子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分配私有副本?

3 个答案:

答案 0 :(得分:2)

N有多大,NN(i)有多强?

您没有分享任何内容,但force[i]可能位于force[i+1]的同一缓存行中。这就是所谓的false sharing,可能非常有害。 OpenMP应该将事情一起批处理以弥补这一点,所以用足够大的N我认为这不是你的问题。

如果NN(i)不是CPU密集型的,那么您可能会遇到一个简单的内存瓶颈 - 在这种情况下,在其上放置更多内核将无法解决任何问题。

答案 1 :(得分: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);
        }
    }
}

这样,你在一个线程上计算了很多东西,在下一个线程上计算了很多东西,每一堆都远远不够,以至于你没有得到错误的共享。

如果您不能这样做,请尝试以其他方式将其拆分,这样每次都会计算出更大的部分。

答案 2 :(得分: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]很远。

如果仍然可扩展性差,请尝试使用英特尔Parallel Amplifier(或VTune)等分析器来查看每个线程需要多少内存带宽。如果是这样,在你的计算机中加入更多的DRAM :)这将真正提高内存带宽。