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?

Antworten auf die Frage(1)

Ihre Antwort auf die Frage