实现OpenMP标准的编译器可能(但没有义务)利用特殊的硬件指令在#pragma omp atomic
指令原子之后进行某些内存更新,从而避免昂贵的锁定。根据{{3}},GCC实现了如下原子更新:
尽可能使用内置的原子更新。如果失败,则尝试进行比较和交换循环。如果这也失败了,则使用表达式周围的常规关键部分。
如何确定给定计算机和GCC版本中实际使用的三个中的哪一个?是否有一些GCC的详细选项,我可以设置找出,而不必分析我的程序或查看生成的字节码?
是否有一些文档列出了提供原子添加/增量/等指令的CPU /架构,允许我预测给定机器的结果?
我在各种不同的机器上使用GCC版本4.2到4.6。
答案 0 :(得分:1)
您可以使用-fdump-tree-all
选项查看中间树表示。给定该选项,GCC在几个中间步骤中写入一组文件,并且可以观察应用于树的连续变换。 .ompexp
文件在这里特别有用,因为它在OpenMP表达式扩展到具体实现之后就包含了树。
例如,以下简单代码中parallel
区域内的块:
int main (void)
{
int i = 0;
#pragma omp parallel
{
#pragma omp atomic
i++;
}
return i;
}
由GCC 4.7.2在64位Linux上转换为:
;; 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;
}
最终以:
结束00000000004006af <main._omp_fn.0>:
4006af: 55 push %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: 48 8b 00 mov (%rax),%rax
4006be: f0 83 00 01 lock addl $0x1,(%rax)
4006c2: 5d pop %rbp
4006c3: c3 retq
至于第二个问题,它可能还取决于GCC是如何建立的。
答案 1 :(得分:0)
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上有原子增量和减量。