4

我有这两段代码

#pragma omp parallel
#pragma omp sections
  {
#pragma omp section
    printf("H");
#pragma omp section
    printf("e");
#pragma omp section
    printf("l");
#pragma omp section
    printf("l");
#pragma omp section
    printf("o");
#pragma omp section
    printf(" ");
#pragma omp section
    printf("W");
#pragma omp section
    printf("o");
#pragma omp section
    printf("r");
#pragma omp section
    printf("l");
#pragma omp section
    printf("d");
#pragma omp section
    printf("!");
  }

char word[] = "Hello World!";
int n;

#pragma omp parallel for
  for(n=0; n<12; n++)
  {
  printf("%c", word[n]);
  }

而第一个总是打印Hello World!第二个有时打印Hello World!有时打印Helld!lo Wor

为什么第一个看起来是确定性的,而另一个却不是?

4

4 回答 4

7

首先,官方的 GCC 4.1.2支持 OpenMP。可能您有一个源自 RedHat 的 Linux 发行版(RHEL、Fedora、CentOS、Scientific Linux 等),它具有从某个较新版本向后移植到 GCC 4.1.2 的 OpenMP 支持。RH 曾经维护该反向移植很长一段时间,直到他们最终切换到更新的 GCC 版本。

写入共享流会导致 OpenMP 部分和并行循环中的行为不确定。您在此处观察到的是sectionsGCC 中实现的动态调度性质的结果。libgomp(GCC OpenMP 运行时)在团队中的线程之间以先到先得的方式分配部分。在您的情况下可能发生的情况是,部分的大小非常短,因此执行时间非常短,以至于在并行区域开始时退出停靠屏障的第一个线程在其他线程甚至赶上之前消耗了所有工作项,导致在所有部分的串行执行中。

至于并行for循环,您观察到的是默认循环调度的结果libgompstatic即 12 次迭代以线性方式平均分配给线程。我猜你的情况有 6 个线程(基于加扰输出中的文本段),所以线程 0 的迭代从 0 到 1,线程 1 的迭代从 2 到 3,依此类推。同样,每个线程中迭代的执行都非常明确,但不能保证线程本身执行的顺序。

请注意,此行为非常特定于 GCC。OpenMP 标准说:

在团队中的线程之间调度结构化块的方法是实现定义的。

例如,英特尔的编译器以循环方式分配这些部分,即,将部分n分配给线程n % num_threads,这很像for具有静态调度和块大小为 1 的并行循环所做的事情。

于 2013-03-26T15:43:52.233 回答
4

如果您希望 for 循环执行相同的操作,请在您的 for 循环中使用“ordered”,如下所示:

#pragma omp parallel for ordered 
for(n=0; n<12; n++) {
    #pragma omp ordered
    printf("%c", word[n]);
}

见这个例子: http ://bisqwit.iki.fi/story/howto/openmp/#ExampleCalculatingTheMandelbrotFractalInParallel

于 2013-03-26T09:23:43.833 回答
2

omp parallel for不是确定性的。您应该使用此子句来实现算法,这些算法在数组中具有独立的数据。如果要打印一些文本,则字母应按顺序一个接一个,但omp parallel for以随机顺序执行代码。

于 2013-03-25T15:11:32.203 回答
2

部分不是确定性的,并行 fors 也不是。这里的 OpenMP 规范 没有说明部分是确定性的。

我用 2 个线程运行了你的部分代码并得到了Hllo World!e

您的代码按顺序生成字母只是偶然的。

于 2013-03-25T22:55:59.537 回答