摘要:我曾预计std::atomic<int*>::load
withstd::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
是原子比较和交换,我们在这里看到原子加载 a char
、 anint
或 an的代码int*
是一个比较和交换循环。我还为 32 位 x86 构建了这段代码,该实现仍然基于lock cmpxchg
.
问题:是否有某些原因std::atomic<int*>::load
需要进行比较和交换循环?