(测试代码位于here,供那些想要阅读或使用它的人使用)。 它包括一个带有
omp_proc_bind(close)
子句的并行区域,以及前面没有的并行区域。在每个并行区域内,它打印实际的线程关联性,以显示是否已实现请求的绑定。 (抱歉,这假设是 Linux,因为我会低调并致电 sched_getaffinity
来调查发生了什么)。
考虑这些代码的执行:-
% OMP_NUM_THREADS=4 ./a.out
omp_get_max_threads() = 4
OMP_PLACES="UNDEFINED", omp_get_num_places() = 0
OMP_PROC_BIND="UNDEFINED", omp_proc_bind() = false
omp_get_num_threads() = 4
0: omp_get_place_num() = -1, {0-287}
1: omp_get_place_num() = -1, {0-287}
2: omp_get_place_num() = -1, {0-287}
3: omp_get_place_num() = -1, {0-287}
Forcing proc_bind(close)
omp_get_num_threads() = 4
0: omp_get_place_num() = -1, {0-287}
1: omp_get_place_num() = -1, {0-287}
2: omp_get_place_num() = -1, {0-287}
3: omp_get_place_num() = -1, {0-287}
这表明显式的
proc_bind
子句已被忽略!
然而,如果我们使用 envirables 强制进行绑定,则显式 proc_bind 子句将受到尊重,并更改预先存在的绑定:-
% OMP_PROC_BIND=spread OMP_NUM_THREADS=4 ./a.out
omp_get_max_threads() = 4
OMP_PLACES="UNDEFINED", omp_get_num_places() = 288
OMP_PROC_BIND="spread", omp_proc_bind() = spread
omp_get_num_threads() = 4
0: omp_get_place_num() = 0, {0}
1: omp_get_place_num() = 72, {72}
2: omp_get_place_num() = 144, {144}
3: omp_get_place_num() = 216, {216}
Forcing proc_bind(close)
omp_get_num_threads() = 4
0: omp_get_place_num() = 0, {0}
1: omp_get_place_num() = 1, {1}
2: omp_get_place_num() = 2, {2}
3: omp_get_place_num() = 3, {3}
AFAICS OpenMP 标准(此处)要求无论环境设置如何,都必须遵守 proc_bind 子句:-
如果并行指令具有 proc_bind 子句,则 proc_bind 子句指定的绑定策略将覆盖 bind-var ICV 的第一个元素指定的策略。
然而,GCC 和 LLVM 似乎都忽略了显式的
proc_bind
子句,除非由于设置了 envirable 而已经存在绑定。
这对我来说似乎是错误的。我是不是错过了什么?
好的,我找到了答案,它是“如果通过 envirable 请求绑定,则可以忽略 proc_bind 子句。”
到达那里的路线是
false
,则 proc_bind
子句将覆盖 bind-var ICV 第一个元素的值;否则,proc_bind
子句无效。” (标准中的此处)。因此,允许实现将 bind-var 的默认设置为
false
,因此,忽略 proc_bind
子句。
这对我来说似乎有些可疑,因为这意味着人们可以编写明确说明其想要的内容的代码,然后在运行时将其忽略,并且,更糟糕的是在调试时,有时会被忽略,而不是其他人,或者与一个编译器,但不是另一个!