Sollte std :: atomic <int *> :: load eine Compare-and-Swap-Schleife durchführen?
Zusammenfassung: Ich hatte das erwartetstd::atomic<int*>::load
mitstd::memory_order_relaxed
wäre der Leistung nahe, wenn nur ein Zeiger direkt geladen wird, zumindest wenn sich der geladene Wert selten ändert. Bei der atomaren Auslastung habe ich eine weitaus schlechtere Leistung als bei einer normalen Auslastung von Visual Studio C ++ 2012 festgestellt. Daher habe ich mich entschlossen, dies zu untersuchen. Es stellt sich heraus, dass die atomare Last als implementiert istvergleichen und tauschen Schleife, die ich vermute, ist nicht die schnellste mögliche Umsetzung.
Frage: Gibt es einen Grund dafür?std::atomic<int*>::load
Müssen Sie eine Compare-and-Swap-Schleife durchführen?
Hintergrund: Ich glaube, dass MSVC ++ 2012 eine Compare-and-Swap-Schleife für die atomare Belastung eines Zeigers basierend auf diesem Testprogramm ausführt:
#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;
}
Ich benutze eine__declspec(noinline)
Funktion, um die Montageanweisungen in Bezug auf die atomare Belastung zu isolieren. Ich habe ein neues MSVC ++ 2012-Projekt erstellt, eine x64-Plattform hinzugefügt, die Release-Konfiguration ausgewählt, das Programm im Debugger ausgeführt und die Demontage untersucht. Stellt sich heraus, dass beidestd::atomic<char>
undstd::atomic<int>
Parameter geben am Ende den gleichen Aufruf anloadRelaxed<int>
- Das muss der Optimierer getan haben. Hier ist die Zerlegung der beiden loadRelaxed-Instanzen, die aufgerufen werden:
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)
Die Anleitunglock cmpxchg
ist atomarvergleichen und tauschen und wir sehen hier, dass der Code zum atomaren Laden eineschar
, einint
oder einint*
ist eine Compare-and-Swap-Schleife. Ich habe diesen Code auch für 32-Bit x86 erstellt und die Implementierung basiert noch auflock cmpxchg
.
Frage: Gibt es einen Grund dafür?std::atomic<int*>::load
Müssen Sie eine Compare-and-Swap-Schleife durchführen?