std::atomic<int*>::load 应该做一个比较和交换循环吗?

Should std::atomic<int*>::load be doing a compare-and-swap loop?

本文关键字:比较 一个 循环 交换 int lt atomic gt load std      更新时间:2023-10-16

摘要:我曾希望,具有std::memory_order_relaxedstd::atomic<int*>::load至少在加载值很少更改时直接直接加载指针的性能。我认为原子负载的性能要比Visual Studio C 2012上的正常负载要差得多,因此我决定调查。事实证明,原子负载是作为比较和swap循环实现的,我怀疑这不是最快的实现。

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

背景:我相信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<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是原子比较和swap,我们在这里看到原子上加载 charintint*的代码是一个比较和swap循环。我还为32位x86构建了此代码,该实现仍然基于lock cmpxchg

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

我不相信放松的原子载荷需要比较和交换。最后,这个std ::原子实现并不能用于我的目的,但是我仍然想拥有界面,因此我使用MSVC的屏障interinss制作了自己的std :: Atomic。对于我的用例,这比默认的std::atomic具有更好的性能。您可以在此处查看代码。对于所有负载和存储的订单,应该将其实现到C 11规格。BTW GCC 4.6在这方面不是更好。我不知道GCC 4.7。