【问题标题】:OpenMP: Run 2 thread groups in parallelOpenMP:并行运行 2 个线程组
【发布时间】:2021-04-07 04:51:20
【问题描述】:

我想让 2 个线程组同时运行。例如,2 个线程正在执行代码块 1,另外 2 个线程正在执行另一个代码段。 OpenMP: Divide all the threads into different groups 这里有一个 stackoverflow 问题,我更改了代码以查看它是否适合我在代码中需要的逻辑。

我有下面的代码。

#include <stdio.h>
#include <omp.h>

#define NUM_THREADS 1

int main(int argc, char **argv) {

    omp_set_nested(1);   /* make sure nested parallism is on */
    int nprocs = omp_get_num_procs();
    int nthreads1 = NUM_THREADS;
    int nthreads2 = NUM_THREADS;

    int t1[nthreads1];

    for (int i=0; i<nthreads1; i++) {
        t1[i] = 0;
    }

#pragma omp parallel default(none) shared(nthreads1, nthreads2, t1) num_threads(2)
    #pragma omp single
    {
        #pragma omp task                                            // section 1
        #pragma omp parallel for num_threads(nthreads1)  shared(t1)
        for (int i=0; i<nthreads1; i++) {
            printf("Task 1: thread %d of the %d children of %d: handling iter %d\n",
                        omp_get_thread_num(), omp_get_team_size(2),
                        omp_get_ancestor_thread_num(1), i);
            t1[i] = 1;
        }
        #pragma omp task                                            // section 2
        #pragma omp parallel for num_threads(nthreads2)  shared(t1, t2)
        for (int j=0; j<nthreads2; j++) {
            while (!t1[j]) {
                printf("Task 2: thread %d of the %d children of %d: handling iter %d\n",
                            omp_get_thread_num(), omp_get_team_size(2),
                            omp_get_ancestor_thread_num(1), j);
            }
        }
    }

    return 0;
}

为了检查我的代码是否同时运行 2 个线程组,我将每个组中的线程计数设置为 1,然后保留一个初始化为 0 的布尔列表。 在第一个代码段中,我将布尔值设置为 1,在第二个代码段中,我检查布尔值以跳出 while 循环。上面的代码似乎只由 1 个线程运行,因为如果线程开始运行第二个代码块/部分,那么它会一直卡在 while 循环中,因为另一个线程没有将布尔值设置为 1。

如何并行运行 2 个线程组?

更新:我的用例:我正在使用 OpenMP 编写一个字数统计映射减少程序。我想要一个线程组 2 读取文件,它将读取行添加到队列中。我想要另一个线程组来处理这些队列中的行并更新链式哈希表中的计数。我已经编写了代码,首先进行读取以制定队列,然后进行映射以从队列中获取数据并生成字数 - 但我想将我的程序更改为有 2 个线程组来并行读取和映射 - - 同时。这就是为什么我制作这个短代码来检查我如何实现 2 个线程组,并行运行执行 2 个不同的代码段。

【问题讨论】:

  • 不管代码的其余部分如何,这里缺少的是一些 #pragma omp flush 指令。只需在第二部分的t1[i] = 1; 行之后添加一个,然后在printf() 之后添加另一个...
  • 除了 Gilles 的评论之外,您还可以使用原子指令(或代价高昂的关键部分)。在现代架构上使用缓存使变量在线程之间保持一致。但是缓存或不同的核心并不总是完全同步,因为这很昂贵。因此,您需要告诉您的处理器您在线程之间执行一些手动通信/同步,以便它可以在内核之间同步变量的值。
  • 你是说上面的代码并行运行2个线程做不同的工作? @吉尔斯?我添加了#pragma omp flush directive,但它似乎不起作用。
  • 因为我在声明#pragma omp 指令时使用的是shared(t1)。在这种情况下,同步真的很重要吗?我的意思是,如果一个线程将布尔值设置为 1,那么另一个线程应该能够跳出 while 循环,对吗?似乎一次只运行一个线程。 @JérômeRichard
  • 是的,同步很重要。您不应假设线程具有内存的同步视图,因为在某些平台上,一个线程可以更改共享变量,而其他线程看不到修改。我无法在我的机器上重现该问题。所以这个问题很可能是平台相关的(这与前面的消息是一致的)。您能否在问题中添加有关您的平台(处理器、系统、编译器等)的信息以及输出示例?除此之外,在这种情况下,您可以尝试使用 OpenMP 部分而不是任务。

标签: c multithreading parallel-processing openmp


【解决方案1】:

似乎可以使用带有 nowait 和 task 指令的单个指令来解决上述问题。下面的方法将任务放到队列中,然后线程从队列中提取工作。因此,理想情况下,2 个线程组将处理 2 个不同的任务,这是问题所要求的。下面是代码;

#include <stdio.h>
#include <omp.h>
#define NUM_THREADS 1

int main(int argc, char **argv) {
    omp_set_nested(1);   /* make sure nested parallism is on */
    int nprocs = omp_get_num_procs();
    int nthreads1 = NUM_THREADS;
    int nthreads2 = NUM_THREADS;
    int t1[nthreads1];
    for (int i=0; i<nthreads1; i++) {
        t1[i] = 0;
    }
    #pragma omp parallel default(none) shared(nthreads1, nthreads2, t1)
    {
        #pragma omp single nowait                                            // section 1
        for (int i=0; i<nthreads1; i++) {
            #pragma omp task
            {
                printf("Task 1: thread %d of the %d children of %d: handling iter %d\n",
                            omp_get_thread_num(), omp_get_team_size(2),
                            omp_get_ancestor_thread_num(1), i);
                t1[i] = 1;
            }
        }
        #pragma omp single nowait                                            // section 2
        for (int j=0; j<nthreads2; j++) {
            #pragma omp task
            {
                while (!t1[j]) {
                    printf("Task 2: thread %d of the %d children of %d: handling iter %d\n",
                                omp_get_thread_num(), omp_get_team_size(2),
                                omp_get_ancestor_thread_num(1), j);
                }
            }
        }
    }
    return 0;
}

此外,您可以简单地在 #pragma omp 构造中使用 if-else 语句来并行运行 2 个线程组

#include <stdio.h>
#include <omp.h>
#define NUM_THREADS 2

int main(int argc, char **argv) {
    omp_set_nested(1);   /* make sure nested parallism is on */
    int nprocs = omp_get_num_procs();
    int nthreads1 = NUM_THREADS/2;
    int nthreads2 = NUM_THREADS/2;
    int t1[nthreads1];
    for (int i=0; i<nthreads1; i++) {
        t1[i] = 0;
    }
    #pragma omp parallel default(none) shared(t1) num_threads(NUM_THREADS) {
        int i = omp_get_thread_num(); // section 1
        if (i<nthreads1) {
            printf("Section 1: thread %d\n",i);
            t1[i] = 1;
        } else {
            int j = i - nthreads1;
            while (!t1[j]) {
                printf("Section 2: thread %d, shift_value %d\n", i, j);
            }
        }
    }
    return 0;
}

【讨论】:

  • 我强烈怀疑这是否能以可移植的方式解决问题...使用另一个 OpenMP 运行时和/或编译器应该会产生不同的结果...确实,我认为使用 2 omp single nowait 而不是一个应该不会对 OpenMP 标准产生任何明显的影响,但它可能会导致运行时的不同行为。请注意,运行时的实现/行为无法保证,因此可能会在(不久的)将来发生变化。
  • 它也适用于单个 nowait,但我真的不知道编译器如何在较低级别处理这些。对于我的用例,两者似乎都有效。 @JérômeRichard
猜你喜欢
  • 2015-02-14
  • 1970-01-01
  • 2015-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-20
  • 1970-01-01
相关资源
最近更新 更多