跨职能部门的OpenMP线程团队的持久性

我有一个简单的程序,我用于物理模拟。 我想知道如何在OpenMP中实现某种线程范例。

int main() { #define steps (100000) for (int t = 0;t < steps; t++) { firstParallelLoop(); secondParallelLoop(); if (!(t%100)) { checkpoint(); } } } void firstParallelLoop() {// In another file.c #pragma omp parallel for for (int i = 0; i < sizeOfSim;i++) { //Some atomic floating point ops. } } 

以前,我使用pthreads并在我的双核笔记本电脑上获得1.7加速。 使用OpenMP时,我似乎无法获得任何加速。 我怀疑问题是线程组/池正在快速创建和销毁,带来了灾难性的影响。

在我的pthreads实现中,我需要确保没有创建新线程,并且我的程序表现为客户端 – 服务器。 在pthreads方案中,main()是一个服务器,并且调用firstParallelLoop将释放触发线程重新处理数据的互斥锁/信号量。

当我看到CPU利用率时,我预计它将超过30%的标记(4核,2是HT),但它保持在27左右……

如何让OpenMP做类似的事情? 如何告诉OpenMP重用我的线程?

GCC OpenMP运行时libgomp通过类似于线程池的东西在POSIX系统上实现线程团队 – 线程仅在遇到第一个并行区域时创建,每个线程运行一个无限的工作循环。 进入和退出并行区域是通过障碍实现的。 默认情况下, libgomp使用busy-waiting和sleeping的组合来实现障碍。 忙等待的数量由OMP_WAIT_POLICY环境变量控制。 如果未指定,则在屏障上等待的线程将忙 – 等待300000次旋转(在100000次旋转/毫秒时为3 ms)然后将进入hibernate状态。 如果OMP_WAIT_POLICY设置为active ,则忙等待时间增加到30000000000次旋转(5分钟,100000次旋转/秒)。 您可以通过将GOMP_SPINCOUNT变量设置为繁忙周期数来微调忙等待时间( libgomp假设大约100000次旋转/毫秒,但根据CPU的不同,它可能会变化5倍)。 你可以像这样完全禁用睡眠:

 OMP_WAIT_POLICY=active GOMP_SPINCOUNT=infinite OMP_NUM_THREADS=... ./program 

这将以某种方式改善线程团队的开始时间,但是以CPU时间为代价,因为空闲线程不会空闲而是忙于等待。

为了消除开销,您应该以更加OpenMP友好的方式重写程序。 您的示例代码可以像这样重写:

 int main() { #define steps (100000) #pragma omp parallel { for (int t = 0; t < steps; t++) { firstParallelLoop(); secondParallelLoop(); if (!(t%100)) { #pragma omp master checkpoint(); #pragma omp barrier } } } } void firstParallelLoop() {// In another file.c #pragma omp for for (int i = 0; i < sizeOfSim; i++) { //Some atomic floating point ops. } } 

请注意以下两点:

  • 并行区域插入主程序中。 尽管如此,它并不是 parallel for 。 团队中的所有线程都将执行外部循环steps
  • firstParallelLoopfor循环通过仅使用omp for并行。 因此,如果在OpenMP并行外部调用它将作为串行循环执行,并且当从并行区域内部调用时它将作为并行执行。 对于secondParallelLoop的循环也应该这样做。

主循环中的屏障用于确保其他线程在开始下一次迭代之前等待检查点完成。