std :: atomic的锁在哪里? [英] Where is the lock for a std::atomic?

查看:216
本文介绍了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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆