[英]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来说,似乎有特殊的LDA
和STL
指令......但我在这里有点超出我的深度。
注意:这是遵循C / C ++ 11映射到我信任的处理器 ,但无法证明ARM的真实性。
无论如何......如果你准备手动滚动原子载荷/商店,那么你可以做得更好。
所以...如果这些事情的速度真的很重要 ,我会倾向于手动滚动,假设有限数量的目标架构,并注意到:
你正在使用gcc特定的东西,所以__sync_compiler()
技巧不会引入额外的可移植性问题。
__sync_xxx
系列已被gcc中更完整的__atomic_xxx
取代,因此如果您将来需要添加其他目标体系结构,那么您可以升级到__atomic_xxx
。
并且,在不太遥远的未来,通常可以使用标准的C11原子,因此可以解决解决可移植性问题的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.