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

时间:2010-11-30 19:15:19

标签: c++ multithreading nested parallel-processing openmp

据我所知,#pragma omp parallel及其变体基本上在多个并发线程中执行以下块,这对应于CPU的数量。当嵌套并行化时 - 并行为并行,并行函数内的并行函数等 - 内部并行化会发生什么?

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

3 个答案:

答案 0 :(得分:10)

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

您需要通过设置OMP_NESTEDomp_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];

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

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

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

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

答案 1 :(得分:8)

对于像密集线性代数这样的东西,所有潜在的并行性已经在一个很好的宽循环中放置在一个地方,你不需要嵌套的并行 - 如果你确实想要防止有(比如说) )真正狭窄的matricies,其中领先的维度可能小于核心的数量,那么您只需要collapse指令,它将多个循环理想地展平为一个。

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

答案 2 :(得分:3)

在外层使用NUM_THREADS(num_groups)子句设置要使用的线程数。如果外部循环的计数为N,并且处理器或核心数为num_cores,请使用num_groups = min(N,num_cores)。在内部级别,您需要为每个线程组设置子线程数,以便子线程的总数等于核心数。因此,如果num_cores = 8,N = 4,则num_groups = 4.在较低级别,每个子线程应使用2个线程(因为2 + 2 + 2 + 2 = 8),因此请使用NUM_THREADS(2)子句。您可以将子线程数收集到一个数组中,每个外部区域线程有一个元素(使用num_groups元素)。

此策略始终可以充分利用您的核心。当N < num_cores发生了一些嵌套并行化。当N> = num_cores时,子线程计数数组包含全1,因此内部循环实际上是串行的。