3

实现 OpenMP 标准的编译器可以但没有义务利用特殊的硬件指令来按照#pragma omp atomic指令原子进行某些内存更新,从而避免昂贵的锁。根据http://gcc.gnu.org/onlinedocs/gccint/OpenMP.html,GCC 实现原子更新如下:

只要有可能,就会使用内置的原子更新。如果失败,则尝试比较和交换循环。如果这也失败了,则使用表达式周围的常规临界区。

  1. 如何确定在给定机器和 GCC 版本上实际使用这三个中的哪一个?是否有一些 GCC 的详细选项,我可以设置它来查找,而无需分析我的程序或查看生成的字节码?

  2. 是否有一些文档列出了提供原子加法/增量/等指令的 CPU/架构,允许我预测给定机器的结果?

我在各种不同的机器上使用 GCC 版本 4.2 到 4.6。

4

2 回答 2

1

-fdump-tree-all您可以使用该选项查看中间树表示。鉴于该选项,GCC 在几个中间步骤写入一组文件,并且可以观察应用于树的连续转换。该.ompexp文件在这里特别有趣,因为它包含在 OpenMP 表达式扩展为其具体实现之后的树。

例如parallel下面简单代码中区域内的块:

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;

}

最终结束于:

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 是如何构建的。

于 2012-12-07T11:32:12.477 回答
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 上还有原子增量和减量。

于 2012-12-07T11:31:40.417 回答