我有一个具有以下结构的代码
#pragma omp parallel
{
#omp for nowait
{
// first for loop
}
#omp for nowait
{
// first for loop
}
#pragma barrier
<-- #pragma omp single/critical/atomic --> not sure
dgemm_(....)
#pragma omp for
{
// yet another for loop
}
}
对于dgemm_,我使用多线程mkl链接。我希望mkl使用所有可用的8个线程。这样做最好的方法是什么?
发布于 2013-12-21 18:39:28
这是嵌套并行的一个例子。它由MKL支持,但只有在使用Intel C/C++编译器构建可执行文件时才能工作。造成这一限制的原因是MKL使用英特尔的OpenMP运行时,不同的OMP运行时并不能很好地相互配合。
一旦解决了这个问题,您应该通过将OMP_NESTED
设置为TRUE
来启用嵌套并行,并通过将MKL_DYNAMIC
设置为FALSE
来禁用MKL对嵌套并行性的检测。如果要使用dgemm_
的进程的数据是共享的,那么您必须从single
构造中调用后者。如果每个线程都处理自己的私有数据,那么您不需要任何同步结构,但是使用多线程MKL也不会给您带来任何好处。因此,我假设你的案子是前者。
概括如下:
#pragma omp single
dgemm_(...);
并与以下人员一起奔跑:
$ MKL_DYNAMIC=FALSE MKL_NUM_THREADS=8 OMP_NUM_THREADS=8 OMP_NESTED=TRUE ./exe
还可以使用适当的调用设置参数:
mkl_set_dynamic(0);
mkl_set_num_threads(8);
omp_set_nested(1);
#pragma omp parallel num_threads(8) ...
{
...
}
不过,我更喜欢使用环境变量。
发布于 2018-09-27 10:35:57
虽然这篇文章有点过时,但我还是想给出一些有用的见解。
从函数的角度看,上面的答案是正确的,但从性能的角度来看,不会给出最好的结果。原因是大多数OpenMP实现在线程到达障碍或没有工作可做时不会关闭它们。相反,线程将进入旋转等待循环,并在等待时继续消耗处理器周期。
在本例中:
#pragma omp parallel
{
#omp for nowait
for(...) {} // first loop
#omp for
for(...) {} // second loop
#pragma omp single
dgemm_(....)
#pragma omp for
for(...) {} // third loop
}
将发生的情况是,即使dgemm
调用在MKL中创建了额外的线程,外部级别的线程仍将积极等待single
构造的结束,因此dgemm
将以较低的性能运行。
这个问题基本上有两种解决办法:
1)列表项使用上述代码,除建议的环境变量外,还禁用活动等待:
$ MKL_DYNAMIC=FALSE MKL_NUM_THREADS=8 OMP_NUM_THREADS=8 OMP_NESTED=TRUE OMP_WAIT_MODE=passive ./exe
2)修改代码以分割并行区域:
#pragma omp parallel
{
#omp for nowait
for(...) {} // first loop
#omp for nowait
for(...) {} // second loop
}
dgemm_(...);
#pragma omp parallel
#pragma omp for nowait
for(...) {} // third loop
}
对于解决方案1,线程立即进入睡眠模式,不消耗周期。缺点是线程必须从这种更深的睡眠状态中醒来,这将增加与自旋等待相比的延迟。
对于解决方案2,线程保持在它们的旋等待循环中,并且很可能在dgemm
调用进入其并行区域时积极等待。附加的连接和分叉也会引入一些开销,但它可能比使用single
结构或解决方案1的初始解决方案的超额订阅要好。
什么是最好的解决方案将清楚地取决于在dgemm
操作中所做的工作量,而与叉/连接的同步开销相比,后者主要由线程计数和内部实现所主导。
https://stackoverflow.com/questions/20715475
复制相似问题