为什么GCC std :: atomic增量会产生低效的非原子组装?

Dam*_*mon 11 gcc atomic c++11

__sync_fetch_and_add使用自己的atomic模板已经使用gcc的Intel兼容内置(如)了很长一段时间." __sync"功能现在被正式视为"遗产".

C++ 11支持std::atomic<>及其后代,因此使用它似乎是合理的,因为它使我的代码符合标准,并且编译器将以平台无关的方式生成最佳代码,这几乎太好了真正.
顺便说一下,我也只需atomic要用文字替换std::atomic.有很多的std::atomic(重:内存模型),我真的不需要,但默认参数采取照顾.

现在是坏消息.事实证明,从我所知道的,生成的代码是......彻底的废话,甚至根本不是原子的.即使是增加一个单个原子变量并把它输出具有不少于5个非内联函数调用的最小例子___atomic_flag_for_address,___atomic_flag_wait_explicit__atomic_flag_clear_explicit(完全优化),并且在另一方面,没有在所生成的可执行的单个原子指令.

是什么赋予了?当然总是存在编译器错误的可能性,但是对于大量的审阅者和用户来说,这种相当激烈的事情通常不会被忽视.这意味着,这可能不是一个错误,而是预期的行为.

这么多函数调用背后的"基本原理"是什么,以及如何在没有原子性的情况实现原子性?

尽可能简单的例子:

#include <atomic>

int main()
{
    std::atomic_int a(5);
    ++a;
    __builtin_printf("%d", (int)a);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

产生以下内容.s:

movl    $5, 28(%esp)     #, a._M_i
movl    %eax, (%esp)     # tmp64,
call    ___atomic_flag_for_address   #
movl    $5, 4(%esp)  #,
movl    %eax, %ebx   #, __g
movl    %eax, (%esp)     # __g,
call    ___atomic_flag_wait_explicit     #
movl    %ebx, (%esp)     # __g,
addl    $1, 28(%esp)     #, MEM[(__i_type *)&a]
movl    $5, 4(%esp)  #,
call    _atomic_flag_clear_explicit  #
movl    %ebx, (%esp)     # __g,
movl    $5, 4(%esp)  #,
call    ___atomic_flag_wait_explicit     #
movl    28(%esp), %esi   # MEM[(const __i_type *)&a], __r
movl    %ebx, (%esp)     # __g,
movl    $5, 4(%esp)  #,
call    _atomic_flag_clear_explicit  #
movl    $LC0, (%esp)     #,
movl    %esi, 4(%esp)    # __r,
call    _printf  #
(...)
.def    ___atomic_flag_for_address; .scl    2;  .type   32; .endef
.def    ___atomic_flag_wait_explicit;   .scl    2;  .type   32; .endef
.def    _atomic_flag_clear_explicit;    .scl    2;  .type   32; .endef
Run Code Online (Sandbox Code Playgroud)

......并且所提到的功能看起来像这样objdump:

004013c4 <__atomic_flag_for_address>:
mov    0x4(%esp),%edx
mov    %edx,%ecx
shr    $0x2,%ecx
mov    %edx,%eax
shl    $0x4,%eax
add    %ecx,%eax
add    %edx,%eax
mov    %eax,%ecx
shr    $0x7,%ecx
mov    %eax,%edx
shl    $0x5,%edx
add    %ecx,%edx
add    %edx,%eax
mov    %eax,%edx
shr    $0x11,%edx
add    %edx,%eax
and    $0xf,%eax
add    $0x405020,%eax
ret    
Run Code Online (Sandbox Code Playgroud)

其他的稍微简单,但我找不到一个真正原子的指令(除了一些在X86 xchg原子的假,但这些似乎是NOP/padding,因为它正在xchg %ax,%ax跟随ret).

我完全不确定需要这样一个相当复杂的功能,以及它是如何制造任何原子的.

chi*_*ill 14

这是一个不合适的编译器构建.

检查你的c++config.h,它看起来像这样,但它没有:

/* Define if builtin atomic operations for bool are supported on this host. */
#define _GLIBCXX_ATOMIC_BUILTINS_1 1

/* Define if builtin atomic operations for short are supported on this host.
   */
#define _GLIBCXX_ATOMIC_BUILTINS_2 1

/* Define if builtin atomic operations for int are supported on this host. */
#define _GLIBCXX_ATOMIC_BUILTINS_4 1

/* Define if builtin atomic operations for long long are supported on this
   host. */
#define _GLIBCXX_ATOMIC_BUILTINS_8 1
Run Code Online (Sandbox Code Playgroud)

根据configure测试定义或不定义这些宏,测试检查主机对__sync_XXX功能的支持.这些测试都在libstdc++v3/acinclude.m4,AC_DEFUN([GLIBCXX_ENABLE_ATOMIC_BUILTINS] ....

在您的安装中,从MEM[(__i_type *)&a]汇编文件中的放置显而易见-fverbose-asm,编译器使用宏atomic_0.h,例如:

#define _ATOMIC_LOAD_(__a, __x)                        \
  ({typedef __typeof__(_ATOMIC_MEMBER_) __i_type;                          \
    __i_type* __p = &_ATOMIC_MEMBER_;                      \
    __atomic_flag_base* __g = __atomic_flag_for_address(__p);          \
    __atomic_flag_wait_explicit(__g, __x);                 \
    __i_type __r = *__p;                           \
    atomic_flag_clear_explicit(__g, __x);                      \
    __r; })
Run Code Online (Sandbox Code Playgroud)

使用正确构建的编译器,使用示例程序,c++ -m32 -std=c++0x -S -O2 -march=core2 -fverbose-asm应该生成如下内容:

movl    $5, 28(%esp)    #, a.D.5442._M_i
lock addl   $1, 28(%esp)    #,
mfence
movl    28(%esp), %eax  # MEM[(const struct __atomic_base *)&a].D.5442._M_i, __ret
mfence
movl    $.LC0, (%esp)   #,
movl    %eax, 4(%esp)   # __ret,
call    printf  #
Run Code Online (Sandbox Code Playgroud)