OpenMP:嵌套并行化有什么好处?

era*_*ran 17 c++ parallel-processing multithreading nested openmp

根据我的理解,#pragma omp parallel它的变化基本上在许多并发线程中执行以下块,这对应于CPU的数量.当嵌套并行化时 - 并行为并行,并行函数内的并行函数等 - 内部并行化会发生什么?

我是OpenMP的新手,我想到的情况可能相当简单 - 将矢量与矩阵相乘.这是在两个嵌套的for循环中完成的.假设CPU的数量小于向量中的元素数量,那么尝试并行运行内部循环是否有任何好处?线程总数是否会大于CPU数量,还是内部循环会顺序执行?

min*_*ang 11

(1)OpenMP中的嵌套并行性:http: //docs.oracle.com/cd/E19205-01/819-5270/aewbc/index.html

您需要通过设置启用嵌套并行性,OMP_NESTED或者omp_set_nested因为默认情况下许多实现都会关闭此功能,即使某些实现也不完全支持嵌套并行性.如果启用,则每当您遇到时parallel for,OpenMP将创建其中定义的线程数OMP_NUM_THREADS.因此,如果是2级并行,则线程总数将为N ^ 2,其中N = OMP_NUM_THREADS.

这种嵌套并行性将导致超额预订(即繁忙线程的数量大于核心),这可能降低加速.在极端情况下,嵌套并行性是递归调用的,线程可能会膨胀(例如,创建1000个线程),而计算机只会浪费时间进行上下文切换.在这种情况下,您可以通过设置动态控制线程数omp_set_dynamic.

(2)矩阵向量乘法的一个例子:代码看起来像:

// Input:  A(N by M), B(M by 1)
// Output: C(N by 1)
for (int i = 0; i < N; ++i)
  for (int j = 0; j < M; ++j)
     C[i] += A[i][j] * B[j];
Run Code Online (Sandbox Code Playgroud)

通常,在外环可能的情况下并行化内环是不好的,因为分叉/连接线程的开销.(尽管许多OpenMP实现预先创建了线程,但它仍然需要一些将任务分派给线程并在并行结束时调用隐式屏障)

你担心的是N <CPU的情况.是的,对,在这​​种情况下,加速将受到N的限制,并且让嵌套并行性肯定会带来好处.

但是,如果N足够大,则代码会导致超额订阅.我只想到以下解决方案:

  • 更改循环结构,以便只存在1级循环.(看起来很可行)
  • 专门化代码:如果N很小,那么做嵌套并行,否则不要这样做.
  • 嵌套并行性omp_set_dynamic.但是,请确保如何omp_set_dynamic控制线程数和线程活动.实施可能会有所不同.

  • 处理小N而不将并行度降低一级的方法就是使用collapse;`#pragma omp parallel for collapse`; `for (int i=0; i&lt;N; ++i)` 等。这在理论上合并了 i 和 j 循环。至于另一个问题,“但是,如果 N 足够大,那么代码会导致超额订阅。” ——不,不会。如果在 i 之后放置并行 for,则主线程将执行 i 循环,并且每次 i 迭代您将执行一个 fork,划分 j-work,然后加入。(尽管如您所说,大多数 OpenMP 实现现在将使用公共线程池。) (2认同)

Jon*_*rsi 8

对于像密集线性代数这样的东西,所有潜在的并行性已经在一个很好的宽循环中放置在一个地方,你不需要嵌套的并行 - 如果你想要防止(比如说)真的很窄的情况如果主要维度可能小于核心数,那么您需要的只是崩溃指令,它将多个循环理论上扁平化为一个.

嵌套并行性适用于那些并行性并非全部暴露出来的情况 - 比如说你想要做两个同时进行的功能评估,每个功能评估都可以有效地利用4个核心,而你有一个8核心系统.您可以在并行部分中调用该函数,并且在函数定义中还有一个额外的,例如,并行的.