【问题标题】:schedule() affecting private thread variables in OpenMP #pragma omp parallel forschedule() 影响 OpenMP #pragma omp parallel for 中的私有线程变量
【发布时间】:2022-01-12 23:19:08
【问题描述】:

我一直在关注和修改 C/C++ 中的 OpenMP 教程,以演示/理解 schedule()#pragma omp parallel for 中的工作原理。 这是我的代码:

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

#define THREADS 4
#define N 100

int main ( ) {
  int i;
  int perThread=0;
  printf("Running %d iterations on %d threads.\n", N, THREADS);
  #pragma omp parallel for num_threads(THREADS) private(perThread) //schedule(static)
  for (i = 0; i < N; i++) {
    perThread++;
    printf("Thread: %d\t loops: %d\n", omp_get_thread_num(), perThread);
    usleep(10000); // to slow the process down a bit 

    //Uncomment below to simulate one thread taking longer on each loop 
    // if(omp_get_thread_num()==1)
    //   sleep(1);
  }
  
  // all threads done
  printf("All done!\n");
  return 0;
}

我将它保存为“schedule_example.cpp”并编译它:

g++ schedule_example.cpp -fopenmp -o SheduleEx

然后我将它与未注释的第 13 行 schedule(static) 进行比较,并再次与 schedule() 的各种选项进行比较,即 schedule(static,25) schedule(static,5) schedule(dynamic) schedule(dynamic,5) schedule(runtime)

调度程序工作,代码演示了差异(特别是当第 20 和 21 行未注释时。)

问题在于,对于schedule() 的某些但不是所有选项,perThread 的起始值对于某些但不是所有线程都发生了更改,这可以在打印输出中看到。

我在几台不同的机器上运行了代码,它们都显示了相似的结果。 我在 Windows 10 笔记本电脑上使用了 WSL,g++ --version 返回: g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0.

以最后 8 行为例。如果schedule() 被注释掉或使用schedule(static) 输出是正确的:

 Thread: 2 loops: 24
 Thread: 0 loops: 24
 Thread: 1 loops: 24
 Thread: 3 loops: 24
 Thread: 2 loops: 25
 Thread: 1 loops: 25
 Thread: 0 loops: 25
 Thread: 3 loops: 25
 All done!

但是,如果我对 shedule() 甚至 schedule(static,25) 使用其他应该给出相同结果的东西,它会编译并运行,但最后几行输出是:

 Thread: 1 loops: 24
 Thread: 3 loops: 24
 Thread: 0 loops: 22010
 Thread: 2 loops: 24
 Thread: 1 loops: 25
 Thread: 3 loops: 25
 Thread: 0 loops: 22011
 Thread: 2 loops: 25
 All done!

问题是perThread 的起始值已设置为 1986 但仅适用于线程 0。

如果我在不重新编译的情况下重新运行它,我会得到类似的结果,总是线程 0 是错误的,大约 22000,但每次的数量不一样。如果我在重新运行之前重新编译它会给出相同的结果。

然后我在 Raspberry Pi 上运行相同的代码,得到了相似但略有不同的结果。 g++ --version 返回: g++ (Raspbian 10.2.1-6+rpi1) 10.2.1 20210110

如果使用schedule(dynamic)schedule(dynamic, X),Raspberry Pi 只会在所有线程上打印出正确的循环值 - 我尝试将 1、5 和 25 作为 X 的值。

如果使用 (static)(static, X),则除线程 0 之外的所有线程的起始值大约为 67321,此数字对于线程 1、2 和 3 始终相同,并且通常但不总是在代码的连续运行之间相同。 (auto) 的行为与 (static) 相同。 然而,(runtime)(static) 相对,只有线程 0 错误,但也关闭了大约 67481 - 但是当连续运行几次时,每次都出现相同数量的错误。

我在另一台装有 Arch Linux 的 PC 上再次运行相同的代码,得到了与 Windows 10 笔记本电脑相似的结果。

就一个实际问题而言,我在编写代码的方式上是否做错了什么?有没有办法确保线程的变量不被改变?

抱歉,这篇文章太长了,但我认为问题的核心是 schedule() 以某种方式影响了 private() 中的变量,因为 parallel for 循环开始时的一些线程,某些时候.

谢谢

【问题讨论】:

    标签: c++ multithreading openmp


    【解决方案1】:

    您应该使用firstprivate(perThread) 而不是private(perThread)。使用private 子句,您的私有变量已声明,但未初始化,因此其值未定义。

    OpenMP specification 你可以读到

    firstprivate 子句声明一个或多个列表项为 对任务私有,并使用以下值初始化它们中的每一个 遇到构造时对应的原始项目。

    所以你必须使用这个子句。

    【讨论】:

    • 谢谢!知道为什么它只是出现问题的线程之一,而且只是有时?
    • 嗯,未初始化变量的值取决于之前的内存包含的内容。如果它充满了零,你就不会注意到它。使用int perThread=1; 尝试您的代码,您会发现打印输出总是不正确。
    • ps:使用-Wall 编译器标志是一个好习惯。在这种情况下它会发出警告:11:7:警告:“perThread”可能在此函数中未初始化使用 [-Wmaybe-uninitialized] 11 | int perThread=0;
    • 你是对的,改变'int perThread = 0;'其他任何东西都不会改变起始值。 '-Wall' 编译器标志并没有给我警告。不过谢谢你的提示!
    • 有趣的是,只有在添加-O3 标志时我才会收到警告
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-22
    • 1970-01-01
    • 2021-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-07
    相关资源
    最近更新 更多