最简单的回答这类问题的方法通常是查看生成的汇编代码,然后从那里开始分析。
编译以下代码(我将您的结构体放大以避免狡猾的编译器花招):
#include <atomic>
struct foo {
double a;
double b;
double c;
double d;
double e;
};
std::atomic<foo> var;
void bar()
{
var.store(foo{1.0,2.0,1.0,2.0,1.0});
}
在clang 5.0.0下,使用-O3会产生以下结果:
请查看godbolt。
bar(): # @bar()
sub rsp, 40
movaps xmm0, xmmword ptr [rip + .LCPI0_0] # xmm0 = [1.000000e+00,2.000000e+00]
movaps xmmword ptr [rsp], xmm0
movaps xmmword ptr [rsp + 16], xmm0
movabs rax, 4607182418800017408
mov qword ptr [rsp + 32], rax
mov rdx, rsp
mov edi, 40
mov esi, var
mov ecx, 5
call __atomic_store
很好,编译器委托使用内置函数(__atomic_store
),但这并没有告诉我们实际发生了什么。然而,由于编译器是开源的,我们可以轻松地找到内置函数的实现(我在https://github.com/llvm-mirror/compiler-rt/blob/master/lib/builtins/atomic.c中找到了它):
void __atomic_store_c(int size, void *dest, void *src, int model) {
#define LOCK_FREE_ACTION(type) \
__c11_atomic_store((_Atomic(type)*)dest, *(type*)dest, model);\
return;
LOCK_FREE_CASES();
#undef LOCK_FREE_ACTION
Lock *l = lock_for_pointer(dest);
lock(l);
memcpy(dest, src, size);
unlock(l);
}
看起来魔法发生在lock_for_pointer()
函数中,让我们来看一下它:
static __inline Lock *lock_for_pointer(void *ptr) {
intptr_t hash = (intptr_t)ptr;
hash >>= 4;
intptr_t low = hash & SPINLOCK_MASK;
hash >>= 16;
hash ^= low;
return locks + (hash & SPINLOCK_MASK);
}
这是我们的解释:原子地址用于生成哈希键,以选择预先分配的锁。
is_lock_free()
的返回值为true
。 - François Andrieuxlock cmpxchg16b
指令,但是gcc7及更高版本仍然返回is_lock_free
为false,即使从技术上讲它是正确的。但是纯负载和纯存储很慢,且纯负载彼此竞争。有关此设计决策的更多详细信息,请参阅在升级到MacPorts gcc 7.3后,is_lock_free()返回false的链接。 - Peter Cordes