如果标准::原子 ::负载是做了比较并交换循环?(Should std::atomic

2019-07-03 13:11发布

总结 :我本来以为std::atomic<int*>::loadstd::memory_order_relaxed将接近的只是直接加载一个指针,至少当负载值很少改变的性能。 我看到了比在Visual Studio的C ++ 2012正常负荷的原子负荷差远了性能,所以我决定调查。 事实证明,原子负载为实现比较并交换循环,我怀疑是不是最快的可能的实现。

:有一些原因std::atomic<int*>::load需要做一个比较并交换循环?

背景 :我认为MSVC ++ 2012正在做基于这个测试程序指针的原子负载比较并交换循环:

#include <atomic>
#include <iostream>

template<class T>
__declspec(noinline) T loadRelaxed(const std::atomic<T>& t) {
  return t.load(std::memory_order_relaxed);
}

int main() {
  int i = 42;
  char c = 42;
  std::atomic<int*> ptr(&i);
  std::atomic<int> integer;
  std::atomic<char> character;
  std::cout
    << *loadRelaxed(ptr) << ' '
    << loadRelaxed(integer) << ' '
    << loadRelaxed(character) << std::endl;
  return 0;
}

我使用__declspec(noinline)函数以分离相关的原子负荷组装说明。 我做了一个新的MSVC ++ 2012项目,增加了一个x64平台,选择发布配置,在调试器中运行程序,看着拆卸。 原来,这两个std::atomic<char>std::atomic<int>参数,最终给同一个呼叫loadRelaxed<int> -这一定有什么优化做到了。 下面是两个loadRelaxed实例是得到所谓的拆卸:

loadRelaxed<int * __ptr64>

000000013F4B1790  prefetchw   [rcx]  
000000013F4B1793  mov         rax,qword ptr [rcx]  
000000013F4B1796  mov         rdx,rax  
000000013F4B1799  lock cmpxchg qword ptr [rcx],rdx  
000000013F4B179E  jne         loadRelaxed<int * __ptr64>+6h (013F4B1796h)  

loadRelaxed<int>

000000013F3F1940  prefetchw   [rcx]  
000000013F3F1943  mov         eax,dword ptr [rcx]  
000000013F3F1945  mov         edx,eax  
000000013F3F1947  lock cmpxchg dword ptr [rcx],edx  
000000013F3F194B  jne         loadRelaxed<int>+5h (013F3F1945h)  

该指令lock cmpxchg是原子比较并交换和我们在这里看到,原子加载代码char ,一个intint*是一个比较并交换循环。 我还内置此代码为32位x86和执行仍是基于lock cmpxchg

:有一些原因std::atomic<int*>::load需要做一个比较并交换循环?

Answer 1:

我不相信,宽松的原子负载需要比较并交换。 这到底的std ::原子的实施不是为我的目的使用,但我还是想有接口,所以我使用MSVC的屏障内在做了我自己的std ::原子。 这比默认性能更好std::atomic对我的使用情况。 你可以看到代码在这里 。 它应该落实到C ++ 11规范的所有排序的加载和存储。 顺便说一句GCC 4.6在这方面是不是更好。 我不知道GCC 4.7。



文章来源: Should std::atomic::load be doing a compare-and-swap loop?