如果我在 main 内部和 #pragma omp parallel 之上声明一个变量 int n=5 ,那么它是由并行区域内的所有线程共享的,所以我的问题是这个 n 变量存储在哪里以及线程如何访问它,其机制是什么后面?
n 是否存储在堆上?或者在主堆栈中或在其他区域中。?请解释一下
正如 @Jérôme-Richard 所说,OpenMP(r) 标准并未指定这一点。然而,至少 LLVM(所以现在大多数供应商编译器)和 GCC 在堆栈上分配一个局部变量,即使它后来被共享。
您可以在编译器资源管理器示例之类的代码中看到这一点,它计算由未修饰的
omp parallel
创建的线程数。 (当然,有很多更简单的方法可以做到这一点,但它是一个相对较小、易于理解的示例)。
int availableThreads()
{
int s = 0;
#pragma omp parallel shared(s)
{
#pragma omp atomic
s += 1;
}
return s;
}
考虑(带注释的)LLVM 生成的代码:-
availableThreads: # @availableThreads
push rax
mov dword ptr [rsp + 4], 0 # Allocate s on the stack and zero it
lea rdi, [rip + .L__unnamed_1]
lea rdx, [rip + availableThreads.omp_outlined]
lea rcx, [rsp + 4] # Pass the address of s in rcx
mov esi, 1
xor eax, eax
call __kmpc_fork_call@PLT
mov eax, dword ptr [rsp + 4]
pop rcx
ret
availableThreads.omp_outlined: # @availableThreads.omp_outlined
lock inc dword ptr [rdx]
ret
.L__unnamed_2:
.asciz ";/app/example.c;availableThreads;6;5;;"
.L__unnamed_1:
.long 0 # 0x0
.long 2 # 0x2
.long 0 # 0x0
.long 38 # 0x26
.quad .L__unnamed_2
我们可以看到
s
在外部函数中被分配和归零,并且其地址被传递到 OpenMP 运行时 fork 函数中。在概述的并行体 (availableThreads.omp_outlined
) 中,我们可以看到运行时作为唯一参数传入的值,并且指针的目标以原子方式递增。
GCC 代码类似(如果您想查看它,请使用 Compiler Explorer 示例,或者看看其他架构上会发生什么)。