繁体   English   中英

使用较旧的gcc __sync builtins进行原子获取/加载和设置/存储

[英]atomic get/load and set/store with older gcc __sync builtins

我正在使用gcc 4.6的(供应商分支),并且需要对无符号整数进行4次基本原子操作

  • 原子增量
  • 原子减量
  • 原子集
  • 原子获得

此gcc版本不支持较新的__atomix_XXX内置,只有__sync内置。

这意味着我可以执行以下操作:

#define ATOMIC_INC(ptr) __sync_fetch_and_add((ptr),1)
#define ATOMIC_DEC(ptr) __sync_fetch_and_sub((ptr),1)
#define ATOMIC_GET(ptr) __sync_fetch_and_add((ptr),0)

但是,我找不到实现#define ATOMIC_SET() ,它会自动设置变量,有没有办法用gcc 4.6.x实现这个目的?

另外,有没有更好的方法来实现上面的ATOMIC_GET() 从原子的角度看,生成的程序集看起来很好,尽管由于实际执行了添加操作,它是次优的。

编辑:有问题的架构是ARMv6,x86和x86_64。

__sync_xxx()是在某些英特尔基元上建模的,在你的x86上,原子加载/存储非常简单,我认为这个集合看起来不完整。

对于原子商店,我认为你坚持使用__sync_val_compare_and_swap() ,虽然像__sync_fetch_and_add()一样负载,但显然是矫枉过正:-(

有“完全内存屏障” __sync_synchronize() ,但我无法发现它的作用(除了通过实验,在x86_64上)! 如果你确切地知道你正在编译什么样的机器,那么你可以玩得很开心,看看......从__sync_synchronize()包含的加载和存储开始。

我可以告诉你,对于x86和x86_64,原子加载不需要任何额外的普通读取。 如果你想要memory_order_seq_cst ,原子商店需要一个mfence ,否则不需要。 但是...... __sync_xxx系列中缺少的另一个东西是编译器障碍......除非那是__sync_synchronize()实际上做的!


稍后添加...

我建议将C / C ++ 11映射到处理器,以便很好地描述在x86 / x86_64,ARM和PowerPC上如何/应该实现原子。

要使用__sync_val_compare_and_swap()作为int的原子存储:

  void a_store(int* p_ai, int val)
  {
    int ai_was ;

    ai_was = *p_ai ;
    do { ai_was = __sync_val_compare_and_swap (p_ai, ai_was, val) ;
  } ;

在你的x86 / x86_64上,对于memory_order_seq_cst(SC),你需要一个LOCK XCHG或一个MOV后跟一个MFENCE ...所以在循环中使用LOCK CMPXCHG有点痛苦。 对于ARM来说,这也有点痛苦,但更是如此:-(


手动滚动原子加载/存储严格来说是勇敢的(或者是蛮干的)......并且,根据__sync_synchronize()在给定机器上实际执行的操作, 可能会也可能不起作用

所以,琐碎的方法是:

   __sync_synchronize() ;
   v = v_atomic ;           // atomic load !
   __sync_synchronize() ;

   __sync_synchronize() ;
   v_atomic = v ;           // atomic store !
   __sync_synchronize() ;

其中,对于x86 / x86_64编译(对我来说,在gcc 4.8 for x86_64上):

    mfence
    mov    xxx, xxx
    mfence

用于装载和存储。 这绝对是安全的(和SC)...对于加载它可能会或可能不会比LOCK XADD更好...对于存储它可能比LOCK CMPXCHG和它周围的循环更好!

如果( 并且仅当 )用于ARM,则编译为:

    dmb
    ldr/str
    dmb

然后那是安全的(和SC)。

现在......对于处理器的x86 / x86_64的,你不需要任何MFENCE 在所有的负载,甚至不SC。 但是你确实需要阻止编译器重新排序。 __sync_synchronize()执行此操作以及种植mfence 对于gcc,您可以使用以下voodoo构造__sync_compiler()

  #define __sync_compiler() __asm__ __volatile__("":::"memory")

我深情地认为__sync_synchronize() (对于x86 / x86_64)是有效的:

  #define __sync_mfence() __asm__ __volatile__("mfence":::"memory")

因为x86 / x86_64的表现非常好,所以您可以:

    __sync_compiler() ;
    v = v_atomic ;            // atomic load -- memory_order_seq_cst
    __sync_compiler() ;

    __sync_compiler() ;
    v_atomic = v ;            // atomic store -- memory_order_seq_cst
    __sync_synchronize() ;

AND ...如果你可以使用memory_order_release,那么你可以用_sync_synchronize()替换剩下的_sync_compiler()

现在,对于ARMv7的......如果( 且仅当 -我没有手臂,所以不能对此进行测试) __sync_synchronize()编译为dmb ,那么我们可以做负载有一点点好:

    __sync_compiler() ;
    v = v_atomic ;            // atomic load
    __sync_synchronize() ;

对于所有内存命令:memory_order_seq_cst和_acquire(和_consume)。

对于memory_order_release,我们可以:

    __sync_synchronize() ;
    v_atomic = v ;            // atomic store -- memory_order_release
    __sync_compiler() ;

对于ARMv8来说,似乎有特殊的LDASTL指令......但我在这里有点超出我的深度。

注意:这是遵循C / C ++ 11映射到我信任的处理器 ,但无法证明ARM的真实性。

无论如何......如果你准备手动滚动原子载荷/商店,那么你可以做得更好。

所以...如果这些事情的速度真的很重要 ,我会倾向于手动滚动,假设有限数量的目标架构,并注意到:

  • 你正在使用gcc特定的东西,所以__sync_compiler()技巧不会引入额外的可移植性问题。

  • __sync_xxx系列已被gcc中更完整的__atomic_xxx取代,因此如果您将来需要添加其他目标体系结构,那么您可以升级到__atomic_xxx

  • 并且,在不太遥远的未来,通常可以使用标准的C11原子,因此可以解决解决可移植性问题的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM