C++ 确定如何实现OpenMP原子指令
实现OpenMP标准的编译器可以(但没有义务)利用特殊的硬件指令,按照C++ 确定如何实现OpenMP原子指令,c++,gcc,openmp,atomic,C++,Gcc,Openmp,Atomic,实现OpenMP标准的编译器可以(但没有义务)利用特殊的硬件指令,按照#pragma omp atomic指令进行某些内存更新,从而避免昂贵的锁。根据,GCC实现如下原子更新: 只要可能,就使用内置的原子更新。如果失败,则尝试比较和交换循环。如果同样失败,则使用表达式周围的常规临界部分 如何确定在给定的机器和GCC版本上实际使用了这三个选项中的哪一个?GCC是否有一些详细的选项,我可以设置这些选项来查找,而不必分析我的程序或查看生成的字节码 是否有一些文档列出了提供原子加法/增量/etc指令的C
#pragma omp atomic
指令进行某些内存更新,从而避免昂贵的锁。根据,GCC实现如下原子更新:
只要可能,就使用内置的原子更新。如果失败,则尝试比较和交换循环。如果同样失败,则使用表达式周围的常规临界部分
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16 1
如果相应的操作可用
一般来说,在支持多个处理器的任何体系结构上都可以进行比较和交换(以CAS或LL/SC的形式)
此外,在x86上还有原子增量和减量。您可以使用
-fdump tree all
选项查看中间树表示。给定该选项,GCC在几个中间步骤编写一组文件,可以观察应用于树的连续转换。这里特别感兴趣的是.ompexp
文件,因为它包含OpenMP表达式扩展到具体实现之后的树
例如,以下简单代码中的并行
区域内的块:
int main (void)
{
int i = 0;
#pragma omp parallel
{
#pragma omp atomic
i++;
}
return i;
}
由64位Linux上的GCC 4.7.2转换为:
;; Function main._omp_fn.0 (main._omp_fn.0, funcdef_no=1, decl_uid=1712, cgraph_uid=1)
main._omp_fn.0 (struct .omp_data_s.0 * .omp_data_i)
{
int D.1726;
int D.1725;
int i [value-expr: *.omp_data_i->i];
int * D.1723;
int * D.1722;
<bb 2>:
D.1722_2 = .omp_data_i_1(D)->i;
D.1723_3 = &*D.1722_2;
__atomic_fetch_add_4 (D.1723_3, 1, 0);
return;
}
;;函数main.\u omp\u fn.0(main.\u omp\u fn.0,funcdef\u no=1,decl\u uid=1712,cgraph\u uid=1)
main._omp_fn.0(struct.omp_data_s.0*.omp_data_i)
{
int D.1726;
int D.1725;
int i[值表达式:*.omp_data_i->i];
int*D.1723;
int*D.1722;
:
D.1722_2=.omp_数据_i_1(D)->i;
D.1723_3=&D.1722_2;
__原子取数加4(D.1723,1,0);
返回;
}
最终的结果是:
0000000000 4006AF:
4006af:55%推送rbp
4006b0:48 89 e5 mov%rsp,%rbp
4006b3:48 89 7d f8 mov%rdi,-0x8(%rbp)
4006b7:48 8b 45 f8 mov-0x8(%rbp),%rax
4006bb:488B 00 mov(%rax),%rax
4006be:f0 83 00 01锁地址$0x1,(%rax)
4006c2:5d pop%rbp
4006c3:c3 retq
至于第二个问题,这也可能取决于GCC是如何构建的。我接受回答第一个问题,这是我的主要目标。