std :: atomic的锁在哪里? [英] Where is the lock for a std::atomic?
问题描述
如果数据结构中包含多个元素,则该原子结构的原子版本不能(始终)是无锁的. 有人告诉我,这对于较大的类型是正确的,因为CPU不能在不使用某种锁的情况下原子地更改数据.
If a data structure has multiple elements in it, the atomic version of it cannot (always) be lock-free. I was told that this is true for larger types because the CPU can not atomically change the data without using some sort of lock.
例如:
#include <iostream>
#include <atomic>
struct foo {
double a;
double b;
};
std::atomic<foo> var;
int main()
{
std::cout << var.is_lock_free() << std::endl;
std::cout << sizeof(foo) << std::endl;
std::cout << sizeof(var) << std::endl;
}
输出(Linux/gcc)是:
the output (Linux/gcc) is:
0
16
16
由于原子和foo
的大小相同,所以我认为原子中没有存储锁.
Since the atomic and foo
are the same size, I don't think a lock is stored in the atomic.
我的问题是:
如果一个原子变量使用锁,它将存储在哪里,这对于该变量的多个实例意味着什么?
My question is:
If an atomic variable uses a lock, where is it stored and what does that mean for multiple instances of that variable ?
推荐答案
回答此类问题的最简单方法通常是查看生成的程序集并将其从那里获取.
The easiest way to answer such questions is generally to just look at the resulting assembly and take it from there.
编译以下内容(我将您的结构放大了,以躲避狡猾的编译器的恶作剧):
Compiling the following (I made your struct larger to dodge crafty compiler shenanigans):
#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上查看
In clang 5.0.0 yields the following under -O3: see on 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
),但这并没有告诉我们这里到底发生了什么.但是,由于编译器是开源的,因此我们可以轻松地找到内在函数的实现(我在
Great, the compiler delegates to an intrinsic (__atomic_store
), that's not telling us what's really going on here. However, since the compiler is open source, we can easily find the implementation of the intrinsic (I found it in 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()
中,让我们来看一下:
It seems like the magic happens in lock_for_pointer()
, so let's have a look at it:
static __inline Lock *lock_for_pointer(void *ptr) {
intptr_t hash = (intptr_t)ptr;
// Disregard the lowest 4 bits. We want all values that may be part of the
// same memory operation to hash to the same value and therefore use the same
// lock.
hash >>= 4;
// Use the next bits as the basis for the hash
intptr_t low = hash & SPINLOCK_MASK;
// Now use the high(er) set of bits to perturb the hash, so that we don't
// get collisions from atomic fields in a single object
hash >>= 16;
hash ^= low;
// Return a pointer to the word to use
return locks + (hash & SPINLOCK_MASK);
}
这是我们的解释:原子的地址用于生成哈希键以选择预先分配的锁.
And here's our explanation: The address of the atomic is used to generate a hash-key to select a pre-alocated lock.
这篇关于std :: atomic的锁在哪里?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!