从openmp并行区域调用多线程MKL

时间:2013-12-21 04:38:42

标签: multithreading openmp intel-mkl

我有一个包含以下结构的代码

#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个线程。这样做的最佳方式是什么?

2 个答案:

答案 0 :(得分:5)

这是嵌套并行性的一种情况。它受MKL支持,但仅当您的可执行文件是使用英特尔C / C ++编译器构建时才有效。这种限制的原因是MKL使用英特尔的OpenMP运行时,并且不同的OMP运行时彼此不能很好地运行。

一旦整理完毕,您应该通过将OMP_NESTED设置为TRUE来启用嵌套并行性,并通过将MKL_DYNAMIC设置为{{1}来禁用MKL检测嵌套并行性}。如果共享要与FALSE进行处理的数据,则必须从dgemm_构造中调用后者。如果每个线程处理自己的私有数据,那么您不需要任何同步构造,但使用多线程MKL也不会给您任何好处。因此,我认为你的案子是前者。

总结:

single

并运行:

#pragma omp single
dgemm_(...);

您还可以使用适当的调用设置参数:

$ MKL_DYNAMIC=FALSE MKL_NUM_THREADS=8 OMP_NUM_THREADS=8 OMP_NESTED=TRUE ./exe

虽然我更愿意使用环境变量。

答案 1 :(得分:2)

虽然这篇文章过时了,但我仍然想对此提供一些有用的见识。

从功能角度来看,以上答案是正确的,但从性能角度来看,并不能给出最佳结果。原因是大多数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构造的结束,从而{{1} }会降低性能。

此问题基本上有两种解决方案:

1)列表项使用上面的代码,除了建议的环境变量外,还禁用活动等待:

dgemm

2)修改代码以分割并行区域:

$ MKL_DYNAMIC=FALSE MKL_NUM_THREADS=8 OMP_NUM_THREADS=8 OMP_NESTED=TRUE OMP_WAIT_MODE=passive ./exe

对于解决方案1,线程立即进入睡眠模式并且不消耗周期。缺点是线程必须从更深的睡眠状态中唤醒,与自旋等待相比,这将增加延迟。

对于解决方案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 } 调用进入其并行区域时主动等待。额外的联接和派生也会带来一些开销,但它可能比使用dgemm构造或解决方案1的初始解决方案的超额预订更好。

最清楚的解决方案是取决于single操作中完成的工作量,而不是fork / join的同步开销,而同步开销主要由线程计数和内部实现控制。 / p>