__sync_xxx() 以一些 Intel 原语为模型,在您的 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 mappings to processors 以很好地描述如何在 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
那么 to 是安全的(和 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() ;
并且...如果您可以使用 memory_order_release,那么您可以将唯一剩余的 _sync_synchronize() 替换为 _sync_compiler() !
现在,对于 ARMv7... 如果(且仅当 -- 我没有 ARM,因此无法测试)__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 mappings to processors,但不能证明 ARM 的真实性。
无论如何...如果您准备手动执行原子加载/存储,那么您可以做得更好。
所以...如果这些东西的速度真的很重要,我会很想手动滚动,假设目标架构数量有限,并注意到:
无论如何你都在使用 gcc 特定的东西,所以__sync_compiler() 技巧不会引入额外的可移植性问题。
__sync_xxx 系列已被 gcc 中更完整的 __atomic_xxx 所取代,因此如果您将来需要添加另一个目标架构,那么您也许可以升级到 __atomic_xxx .
而且,在不远的将来,标准 C11 原子将普遍可用,因此可以解决可移植性问题。