【发布时间】:2020-06-27 06:59:50
【问题描述】:
我一直在尝试了解 OpenMP 并行 for 循环在与临界区和有序指令结合时的工作原理。有几个代码示例让我感到困惑:
1. OpenMP 并行 for 循环用于使用循环索引 i 和线程 ID 初始化数组 s。没有使用ordered 指令或临界区。
#include <stdio.h>
#include <omp.h>
#define N 10
#define CHUNKSIZE 1
int main(int argc, char* argv[])
{
int i, chunk = CHUNKSIZE;
char s[N][22];
#pragma omp parallel for shared(s,chunk) private(i) schedule(static, chunk)
for (i = 0; i < N; ++i)
{
int tid = omp_get_thread_num();
sprintf(s[i], "%d:%d", i, tid);
printf("i: %d tid: %d\n", i, tid);
}
puts("\nArray initialization order:");
for (i = 0; i < N; ++i)
puts(s[i]);
}
它打印以下内容:
i: 7 tid: 7
i: 4 tid: 4
i: 5 tid: 5
i: 6 tid: 6
i: 0 tid: 0
i: 8 tid: 0
i: 3 tid: 3
i: 1 tid: 1
i: 2 tid: 2
i: 9 tid: 1
Array initialization order:
0:0
1:1
2:2
3:3
4:4
5:5
6:6
7:7
8:0
9:1
我无法弄清楚为什么s 包含i 索引(第一个数字),尽管没有ordered 指令,为什么printf("i: %d tid: %d\n", i, tid) 以不同的顺序显示它们?
2. 将ordered 添加到omp parallel for 子句似乎不会改变任何东西,除非将omp ordered 放在循环体中。
#pragma omp parallel for shared(s,chunk) private(i) schedule(static, chunk) ordered
for (i = 0; i < N; ++i)
{
int tid = omp_get_thread_num();
sprintf(s[i], "%d:%d", i, tid);
printf("i: %d tid: %d\n", i, tid);
}
产生与之前相同的结果:sprintf(s[i], "%d:%d", i, tid) 使用严格的i 序列初始化数组,而printf("i: %d tid: %d\n", i, tid) 以任意顺序打印i。
#pragma omp parallel for shared(s,chunk) private(i) schedule(static, chunk) ordered
for (i = 0; i < N; ++i)
{
int tid = omp_get_thread_num();
sprintf(s[i], "%d:%d", i, tid);
#pragma omp ordered
printf("i: %d tid: %d\n", i, tid);
}
现在一切都按照i的顺序发生:
i: 0 tid: 0
i: 1 tid: 1
i: 2 tid: 2
i: 3 tid: 3
i: 4 tid: 4
i: 5 tid: 5
i: 6 tid: 6
i: 7 tid: 7
i: 8 tid: 0
i: 9 tid: 1
Array initialization order:
0:0
1:1
2:2
3:3
4:4
5:5
6:6
7:7
8:0
9:1
再说一次,我不明白为什么我们需要将 omp ordered 放在循环体中以强制执行打印顺序,而数组初始化不需要这样做。
3. 使用临界区确保一次只有一个线程执行循环体:
#pragma omp parallel for shared(s,chunk) private(i) schedule(static, chunk) ordered
for (i = 0; i < N; ++i)
#pragma omp critical
{
int tid = omp_get_thread_num();
sprintf(s[i], "%d:%d", i, tid);
printf("i: %d tid: %d\n", i, tid);
}
同样,以任意顺序打印i,并以i的严格顺序初始化s:
i: 1 tid: 1
i: 4 tid: 4
i: 3 tid: 3
i: 2 tid: 2
i: 5 tid: 5
i: 0 tid: 0
i: 7 tid: 7
i: 6 tid: 6
i: 8 tid: 0
i: 9 tid: 1
Array initialization order:
0:0
1:1
2:2
3:3
4:4
5:5
6:6
7:7
8:0
9:1
这完全令人困惑,因为据我了解,临界区必须保证 sprintf 和 printf 语句由同一个线程执行而没有任何中断。
我们将不胜感激任何帮助解决这个问题。
【问题讨论】:
标签: c++ multithreading loops for-loop openmp