【问题标题】:Nested Parallelism : Why only the main thread runs and executes the parallel for loop four times?嵌套并行:为什么只有主线程运行并执行并行 for 循环四次?
【发布时间】:2021-03-15 01:41:41
【问题描述】:

我的代码:

#include <cstdio>
#include "omp.h"

int main() {
    omp_set_num_threads(4);  
    #pragma omp parallel
    {
        #pragma omp parallel for 
        for (int i = 0; i < 6; i++)
        {
            printf("i = %d, I am Thread %d\n", i, omp_get_thread_num());
        }    
    }
    return 0;
}

我得到的输出:

i = 0, I am Thread 0
i = 1, I am Thread 0
i = 2, I am Thread 0
i = 0, I am Thread 0
i = 1, I am Thread 0
i = 0, I am Thread 0
i = 1, I am Thread 0
i = 2, I am Thread 0
i = 2, I am Thread 0
i = 3, I am Thread 0
i = 0, I am Thread 0
i = 1, I am Thread 0
i = 3, I am Thread 0
i = 4, I am Thread 0
i = 5, I am Thread 0
i = 2, I am Thread 0
i = 3, I am Thread 0
i = 4, I am Thread 0
i = 5, I am Thread 0
i = 3, I am Thread 0
i = 4, I am Thread 0
i = 5, I am Thread 0
i = 4, I am Thread 0
i = 5, I am Thread 0

添加“并行”是问题的原因,但我不知道如何解释。

我的问题是:为什么只有主线程并且运行了四次for循环?

【问题讨论】:

    标签: c multithreading parallel-processing openmp


    【解决方案1】:

    默认情况下,nested parallelism禁用。尽管如此,您可以通过以下任一方式明确启用 nested parallelism

       omp_set_nested(1);
    

    或者通过将OMP_NESTED 环境变量设置为true。

    同样从OpenMP standard我们知道:

    当一个线程遇到一个并行结构时,一组线程是 创建以执行并行区域。 遇到并行构造的线程变为 新团队的主线程,线程号为零 新平行区域的持续时间。新团队中的所有线程, 包括主线程,执行区域。一旦球队在 创建后,团队中的线程数保持不变 该平行区域的持续时间。

    来自source,您可以阅读以下内容。

    OpenMP 并行区域可以相互嵌套。 如果嵌套 并行性被禁用,然后由一个线程创建的新团队 在平行区域内遇到平行构造包括 只有遇到的线程。如果启用了嵌套并行, 那么新团队可能包含多个线程。

    这解释了为什么当您添加第二个parallel region 时只有一个线程每个 团队执行封闭代码(即, for 循环)。换句话说,从第一个parallel region4线程被创建,这些线程中的每一个在遇到第二个parallel region时都会创建一个新团队并成为该团队的主人(即,将拥有ID=0在新创建的团队中)。 但是,由于您没有显式启用嵌套并行性,因此每个团队仅由一个线程组成。因此,4 拥有一个线程的团队将执行 for 循环。因此,您将有以下语句:

    printf("i = %d, I am Thread %d\n", i, omp_get_thread_num());
    

    正在打印6 x 4 = 24 times循环迭代的总数乘以4 团队中的线程总数)。下图提供了该流程的可视化:

    如果在第一个parallel region和第二个parallel region之间添加printf语句,如下:

    int main() {
    
        omp_set_num_threads(4);
        #pragma omp parallel
        {
           printf("Before nested parallel region:  I am Thread{%d}\n", omp_get_thread_num());
           #pragma omp parallel for // Adding "parallel" is the cause of the problem, but I don't know how to explain it.
           for (int i = 0; i < 6; i++)
           {
               printf("i = %d, I am Thread %d\n", i, omp_get_thread_num());
           }
        }
        return 0;
    }
    

    您会得到类似于以下输出的内容(请记住,输出前4 行的顺序是不确定的)。

    Before nested parallel region:  I am Thread{1}
    Before nested parallel region:  I am Thread{0}
    Before nested parallel region:  I am Thread{2}
    Before nested parallel region:  I am Thread{3}
    i = 0, I am Thread 0
    i = 0, I am Thread 0
    i = 0, I am Thread 0
    (...)
    i = 5, I am Thread 0
    

    这意味着在第一个parallel region 内(但仍在第二个并行区域之外)有一个由 4 个线程组成的团队——IDs0 变化到3——并行执行。因此,这些线程中的每一个都将执行printf 语句:

    printf("I am Thread outside the nested region {%d}\n", omp_get_thread_num());
    

    并为omp_get_thread_num() 方法调用显示不同的值。

    如前所述,嵌套并行被禁用。因此,当这些线程中的每一个遇到第二个parallel region 时,每个线程都会创建一个新团队并成为主人(将在新创建的团队中拥有ID=0)。 - 也是该团队中唯一的成员。因此,为什么声明

     printf("i = %d, I am Thread %d\n", i, omp_get_thread_num());
    

    在循环内部,总是输出(..) I am Thread 0,因为在这个上下文中的方法omp_get_thread_num()总是返回0。然而,即使omp_get_thread_num() 方法返回了0,它并不意味着代码是按顺序执行的(由具有ID=0 的线程执行),而是每个master4 团队正在返回他们的ID=0

    如果您启用了嵌套并行,您将获得如下图所示的流程:

    为简单起见,省略了线程 13 的执行,但它与线程 0 相同。

    因此,从第一个 parallel region 开始,创建了一个具有 4 线程的团队。在遇到下一个 parallel region 来自上一个团队的每个线程后,将创建一个新的团队,每个团队都有 4 线程,所以目前我们共有 16 线程跨越 4 团队。最后,每个团队将执行整个 for 循环。但是,因为您有一个#pragma omp parallel for 构造函数,所以 for 循环的迭代将在每个团队内的线程之间分配。

    请记住,在上图中,我假设循环之间的迭代有一定的static 循环分布,我并不是暗示循环迭代将始终像这样在 OpenMP 标准的所有实现中划分。

    【讨论】:

      猜你喜欢
      • 2017-09-20
      • 2017-04-18
      • 2012-05-19
      • 1970-01-01
      • 2020-07-17
      • 1970-01-01
      • 2018-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多