2

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

  • 使用 Linux 工具显示 CPU 使用率top,运行 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 分配私有副本吗?

4

3 回答 3

2

有多大N,有多密集NN(i)

您说没有共享,但force[i]可能在force[i+1]. 这就是所谓的虚假共享,可能非常有害。OpenMP 应该将事情一起批处理来弥补这一点,所以如果足够大,N我认为这不是你的问题。

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

于 2012-12-27T21:17:35.320 回答
1

假设 force[i] 是 4 或 8 字节数据的普通数组,你肯定有错误的共享,毫无疑问。

假设 function(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);
        }
    }
}

这样一来,你在一个线程上计算一堆东西,在下一个线程上计算一堆东西,每束东西之间的距离足够远,你不会得到错误的共享。

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

于 2012-12-27T21:46:23.213 回答
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 :) 这将真正提高内存带宽。

于 2013-01-11T00:11:31.580 回答