首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >从openmp并行区域调用多线程MKL

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

Stack Overflow用户
提问于 2013-12-21 04:38:41
回答 2查看 3.7K关注 0票数 5

我有一个具有以下结构的代码

代码语言:javascript
运行
复制
#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个线程。这样做最好的方法是什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-12-21 18:39:28

这是嵌套并行的一个例子。它由MKL支持,但只有在使用Intel C/C++编译器构建可执行文件时才能工作。造成这一限制的原因是MKL使用英特尔的OpenMP运行时,不同的OMP运行时并不能很好地相互配合。

一旦解决了这个问题,您应该通过将OMP_NESTED设置为TRUE来启用嵌套并行,并通过将MKL_DYNAMIC设置为FALSE来禁用MKL对嵌套并行性的检测。如果要使用dgemm_的进程的数据是共享的,那么您必须从single构造中调用后者。如果每个线程都处理自己的私有数据,那么您不需要任何同步结构,但是使用多线程MKL也不会给您带来任何好处。因此,我假设你的案子是前者。

概括如下:

代码语言:javascript
运行
复制
#pragma omp single
dgemm_(...);

并与以下人员一起奔跑:

代码语言:javascript
运行
复制
$ MKL_DYNAMIC=FALSE MKL_NUM_THREADS=8 OMP_NUM_THREADS=8 OMP_NESTED=TRUE ./exe

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

代码语言:javascript
运行
复制
mkl_set_dynamic(0);
mkl_set_num_threads(8);
omp_set_nested(1);

#pragma omp parallel num_threads(8) ...
{
   ...
}

不过,我更喜欢使用环境变量。

票数 6
EN

Stack Overflow用户

发布于 2018-09-27 10:35:57

虽然这篇文章有点过时,但我还是想给出一些有用的见解。

从函数的角度看,上面的答案是正确的,但从性能的角度来看,不会给出最好的结果。原因是大多数OpenMP实现在线程到达障碍或没有工作可做时不会关闭它们。相反,线程将进入旋转等待循环,并在等待时继续消耗处理器周期。

在本例中:

代码语言:javascript
运行
复制
#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)列表项使用上述代码,除建议的环境变量外,还禁用活动等待:

代码语言:javascript
运行
复制
$ MKL_DYNAMIC=FALSE MKL_NUM_THREADS=8 OMP_NUM_THREADS=8 OMP_NESTED=TRUE OMP_WAIT_MODE=passive ./exe

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

代码语言:javascript
运行
复制
#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操作中所做的工作量,而与叉/连接的同步开销相比,后者主要由线程计数和内部实现所主导。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20715475

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档