我有一个简单的程序,我用于物理模拟。我想知道如何在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重用我的线程?
答案 0 :(得分:6)
GCC OpenMP运行时libgomp
通过类似于线程池的东西在POSIX系统上实现线程团队 - 线程仅在遇到第一个并行区域时创建,每个线程运行一个无限的工作循环。进入和退出并行区域是通过障碍实现的。默认情况下,libgomp
使用忙等待和休眠的组合来实现障碍。忙等待的数量由OMP_WAIT_POLICY
环境变量控制。如果未指定,则在屏障上等待的线程将忙 - 等待300000次旋转(在100000次旋转/毫秒时为3 ms)然后将进入休眠状态。如果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
次。for
使firstParallelLoop
中的omp for
循环并行。因此,如果在OpenMP并行外部调用它将作为串行循环执行,并且当从并行区域内部调用时它将作为并行执行。对secondParallelLoop
。主循环中的障碍用于确保其他线程在开始下一次迭代之前等待检查点完成。