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

时间:2012-11-03 20:17:53

标签: c++ visual-c++ visual-studio-2012 atomic compare-and-swap

摘要:我曾预计std::atomic<int*>::loadstd::memory_order_relaxed的关系会接近直接加载指针的性能,至少在加载的值很少发生变化时。我看到原子负载的性能远远低于Visual Studio C ++ 2012上的正常负载,因此我决定进行调查。事实证明原子负载是作为compare-and-swap循环实现的,我怀疑这不是最快的实现。

问题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是原子compare-and-swap,我们在此处看到原子加载charintint*的代码是比较 - 交换循环。我还为32位x86构建了此代码,该实现仍基于lock cmpxchg

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

1 个答案:

答案 0 :(得分:1)

我不相信放松的原子载荷需要比较和交换。最后这个std :: atomic实现不能用于我的目的,但我仍然想要接口,所以我使用MSVC的屏障内部函数创建了自己的std :: atomic。对于我的用例,这比默认的std::atomic具有更好的性能。您可以看到代码here。对于加载和存储的所有排序,它应该被实现为C ++ 11规范。 Btw GCC 4.6在这方面并不是更好。我不知道GCC 4.7。