std :: atomic <int*> :: load应该进行比较和交换循环吗?

Bja*_*une 5 c++ atomic visual-c++ compare-and-swap visual-studio-2012

简介:我曾预料到,只要加载的值很少改变std::atomic<int*>::load,std::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;
}
Run Code Online (Sandbox Code Playgroud)

我正在使用一个__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)  
Run Code Online (Sandbox Code Playgroud)

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)  
Run Code Online (Sandbox Code Playgroud)

该指令lock cmpxchg是原子比较和交换,我们在这里看到原子加载a char,an int或an 的代码int*是比较和交换循环.我还为32位x86构建了这个代码,并且该实现仍然基于lock cmpxchg.

问题:是否有某些原因std::atomic<int*>::load需要进行比较和交换循环?

Bja*_*une 1

我不认为宽松的原子负载需要比较和交换。最后这个 std::atomic 实现无法满足我的目的,但我仍然想拥有这个接口,所以我使用 MSVC 的屏障内在函数创建了自己的 std::atomic 。std::atomic这比我的用例的默认性能更好。您可以在此处查看代码。它应该按照 C++11 规范来实现所有加载和存储的顺序。顺便说一句,GCC 4.6 在这方面并没有更好。我不知道 GCC 4.7。