2012-11-03 12 views
5

Zusammenfassung: Ich hatte erwartet, dass std::atomic<int*>::load mit std::memory_order_relaxed nahe an der Leistung wäre, nur einen Zeiger direkt zu laden, zumindest wenn sich der geladene Wert selten ändert. Ich sah weit schlechtere Leistung für die atomare Last als eine normale Last auf Visual Studio C++ 2012, also entschied ich mich zu untersuchen. Es stellt sich heraus, dass die atomare Last als eine compare-and-swap-Schleife implementiert ist, die vermutlich nicht die schnellste Implementierung ist.Sollte std :: atomic <int*> :: Laden eine Vergleichs- und Tausch-Schleife tun?

Frage: Gibt es einen Grund, dass std::atomic<int*>::load eine Vergleichs- und Tauschschleife durchführen muss?

Hintergrund: Ich glaube, dass MSVC++ 2012 ist eine Vergleichs- und Swap-Schleife auf atomare Last eines basierend auf diesem Testprogramm Zeiger zu tun: Um

#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 verwende eine __declspec(noinline) Funktion in um die Montageanweisungen bezüglich der atomaren 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 Disassemblierung betrachtet. Stellt sich heraus, dass beide std::atomic<char> und std::atomic<int> Parameter am Ende geben den gleichen Anruf an - dies muss etwas sein, das der Optimierer getan hat. Hier ist die Demontage der beiden loadRelaxed instantiations, die aufgerufen:

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 Anweisung lock cmpxchg Atom ist compare-and-swap und wir sehen hier, dass der Code zum atomaren Laden einer char, einer int oder int* ist eine Compare-and-Swap-Schleife. Ich habe auch diesen Code für 32-Bit x86 gebaut und diese Implementierung basiert immer noch auf lock cmpxchg.

Frage: Gibt es einen Grund, dass std::atomic<int*>::load eine Vergleichs- und Tauschschleife durchführen muss?

+0

Ich würde auch gerne sehen, warum diese Art von Code generiert wird – James

+0

@James Ich vermute, dass MS hat noch nicht die Zeit, dies besser zu implementieren. Aus meiner eigenen Implementierung heraus dauerte es nur eine winzige Menge an Code, um dies schneller zu machen, aber es brauchte viel Aufwand, um im Detail zu verstehen, was dieser Code tun sollte und wie er mit einer bestimmten Hardware-Plattform interagiert. Ich bin auf Material angewiesen, das von anderen Leuten geschrieben wurde, aber um * wirklich * sicher zu sein, dass du es richtig gemacht hast, denke ich, dass es notwendig sein würde, mit Hardwareherstellern in Kontakt zu treten und viel Zeit mit dem Studium des Standards zu verbringen. Compare-and-Swap ist viel einfacher zu machen und sicherlich zu korrigieren. –

+0

Siehe http://connect.microsoft.com/VisualStudio/Feedback/Details/770885/std-Atomic-Load-Implementierung-ist-absurd-langsam –

Antwort

1

Ich glaube nicht, dass entspannte atomare Lasten vergleichen und austauschen müssen. Am Ende war diese std :: atomic-Implementierung für meinen Zweck nicht verwendbar, aber ich wollte immer noch die Schnittstelle haben, also habe ich meine eigene std :: atomic mit MSVC's Barriereingens gemacht. Dies hat eine bessere Leistung als die Standard std::atomic für meinen Anwendungsfall. Sie können den Code here sehen. Es soll in die C++ 11 Spezifikation für alle Bestellungen für Laden und Speichern implementiert werden. Btw GCC 4.6 ist in dieser Hinsicht nicht besser. Ich weiß nichts über GCC 4.7.