我正在开发一个很好的工具,它需要两个不同的64位值的原子交换。在amd64架构上,可以使用XCHGQ
指令(参见doc中的警告:它是一个很长的pdf)。
相应地,gcc有一些原子内置函数,理想情况下也是如此,例如here可见。
使用这两个文档我生成了以下简单的C函数,用于两个64位值的原子交换:
void theExchange(u64* a, u64* b) {
__atomic_exchange(a, b, b, __ATOMIC_SEQ_CST);
};
(顺便说一下,我并不清楚,为什么需要一个"原子交换" 3个操作数。)
对我来说有点可疑,gcc __atomic_exchange
宏使用3个操作数,所以我测试了它的asm输出。我用gcc -O6 -masm=intel -S
编译了这个,我得到了以下输出:
.LHOTB0:
.p2align 4,,15
.globl theExchange
.type theExchange, @function
theExchange:
.LFB16:
.cfi_startproc
mov rax, QWORD PTR [rsi]
xchg rax, QWORD PTR [rdi] /* WTF? */
mov QWORD PTR [rsi], rax
ret
.cfi_endproc
.LFE16:
.size theExchange, .-theExchange
.section .text.unlikely
正如我们所看到的,结果函数不仅包含单个数据移动,还包含三个不同的数据移动。因此,正如我所理解的那样,这个函数不是真正的原子。
怎么可能?也许我误解了一些文档?我承认,gcc内置文档对我来说并不是很清楚。
答案 0 :(得分:2)
这是__atomic_exchange_n (type *ptr, type val, int memorder)
的通用版本,其中只有ptr
上的交换操作是原子的,val
的读取不是。在通用版本中,val
是通过指针访问的,但原子性仍然不适用于它。当编译器必须调用外部帮助程序时,指针是这样的,它将使用多种大小:
四个非算术函数(加载,存储,交换和 compare_exchange)也都有通用版本。这个通用 版本适用于任何数据类型。它使用无锁内置 如果特定数据类型大小使这成为可能; 否则,将在运行时解析外部呼叫。这个 外部调用与添加'size_t'的格式相同 插入的参数作为指示大小的第一个参数 被指向的对象。所有对象的大小必须相同。