C++ 确定如何实现OpenMP原子指令

C++ 确定如何实现OpenMP原子指令,c++,gcc,openmp,atomic,C++,Gcc,Openmp,Atomic,实现OpenMP标准的编译器可以(但没有义务)利用特殊的硬件指令,按照#pragma omp atomic指令进行某些内存更新,从而避免昂贵的锁。根据,GCC实现如下原子更新: 只要可能,就使用内置的原子更新。如果失败,则尝试比较和交换循环。如果同样失败,则使用表达式周围的常规临界部分 如何确定在给定的机器和GCC版本上实际使用了这三个选项中的哪一个?GCC是否有一些详细的选项,我可以设置这些选项来查找,而不必分析我的程序或查看生成的字节码 是否有一些文档列出了提供原子加法/增量/etc指令的C

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

只要可能,就使用内置的原子更新。如果失败,则尝试比较和交换循环。如果同样失败,则使用表达式周围的常规临界部分

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

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

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

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

    我接受回答第一个问题,这是我的主要目标。