C++ Wie wird Release-and-Acquire auf x86 nur mit MOV erreicht?

Lesezeit: 4 Minuten

C Wie wird Release and Acquire auf x86 nur mit MOV erreicht
Benutzer997112

Diese Frage ist eine Nachbereitung/Klarstellung dazu:

Implementiert der MOV x86-Befehl einen C++11-Memory_order_release-Atomspeicher?

Darin heißt es MOV Die Assembler-Anweisung reicht aus, um die Acquiring-Release-Semantik auf x86 auszuführen. Brauchen wir nicht LOCK, Zäune o xchg usw. Ich habe jedoch Schwierigkeiten zu verstehen, wie dies funktioniert.

In Intel doc Vol 3A Kapitel 8 heißt es:

https://software.intel.com/sites/default/files/managed/7c/f1/253668-sdm-vol-3a.pdf

In einem System mit einem Prozessor (Kern) ….

  • Reads werden nicht mit anderen Reads neu geordnet.
  • Schreibvorgänge werden nicht mit älteren Lesevorgängen neu geordnet.
  • Schreibvorgänge in den Speicher werden nicht mit anderen Schreibvorgängen neu geordnet, mit den folgenden Ausnahmen:

aber das ist für einen einzelnen Kern. Der Multi-Core-Abschnitt scheint nicht zu erwähnen, wie Lasten erzwungen werden:

In einem Mehrprozessorsystem gelten die folgenden Ordnungsprinzipien:

  • Einzelne Prozessoren verwenden dieselben Ordnungsprinzipien wie in einem Einzelprozessorsystem.
  • Schreibvorgänge durch einen einzelnen Prozessor werden von allen Prozessoren in der gleichen Reihenfolge beobachtet.
  • Schreibvorgänge von einem einzelnen Prozessor werden NICHT in Bezug auf die Schreibvorgänge von anderen Prozessoren geordnet.
  • Die Erinnerungsordnung gehorcht der Kausalität (die Erinnerungsordnung respektiert die transitive Sichtbarkeit).
  • Jegliche zwei Speicherungen werden von anderen Prozessoren als denen, die die Speicherungen durchführen, in einer konsistenten Reihenfolge gesehen
  • Gesperrte Anweisungen haben eine Gesamtordnung.

Also wie kann MOV allein kann Acquisition-Release erleichtern?

  • Ist nicht MOV eher fortlaufend konsistent von selbst als Putten rel-acq Zäune? Denn es wird nur unter sehr eingeschränkten Bedingungen nachbestellt. Es erinnert mich an Herb Sutters sehr aufschlussreiche Präsentation des SC-DRF-Speichermodells vor langer Zeit.

    – Dekan Seo

    20. Februar 20 um 7:04 Uhr


  • @DeanSeo: Nein, das Hardwarespeichermodell von x86 ist SC + ein Speicherpuffer mit Speicherweiterleitung. Dies ist wie acq_rel, nicht SC.

    – Peter Cordes

    20. Februar 20 um 7:16 Uhr

  • @PeterCordes Interessant! Danke für die Korrektur!

    – Dekan Seo

    20. Februar 20 um 7:25 Uhr

  • @ user997112: Ich erwähne mfence im Kontext dessen, was für sequentielle Konsistenz (SC alias seq_cst) auf x86 benötigt wird. Ich habe es erwähnt, um darauf hinzuweisen, dass alles, was mfence tut, lokal ist, innerhalb des Kerns, der es ausführt. Danke für den Hinweis auf die mögliche Verwirrung, wie ich das erklärt habe, ich sehe es jetzt; Aktualisiert.

    – Peter Cordes

    20. Februar 20 um 22:01 Uhr

  • @ user997112: Hä? Bei acq-rel geht es um die Bestellung anderer Lasten/Speicher relativ zu dieser. Schreiben Sie dann zB einen großen Puffer data_ready.store(true, mo_release);. Ein Leser, der das tut data_ready.load(mo_acquire) und sieht true kann dann den Puffer sicher lesen, selbst wenn der Puffer nicht atomar ist. Wenn Sie nur eine 64-Bit-Shared-Variable haben, brauchen Sie nichts anderes zu ordnen, nur mo_relaxed für diese eine lock-freie Variable.

    – Peter Cordes

    21. Februar 20 um 0:38 Uhr

  • @ user997112: andere als mfence? Die Anwendungsfälle für SFENCE sind nur, wenn Sie schwach geordnete NT-Speicher verwendet haben und diese mit einem “data-ready=true” “freigeben” möchten. Die Anwendungsfälle für LFENCE sind im Grunde nicht vorhanden. Intel hatte möglicherweise Pläne, schwach geordnete Lasten einzuführen, hat dies aber nie getan (außer SSE4.1 movntdqa aus WC-Speicher, wie Video-RAM). Wann sollte ich _mm_sfence, _mm_lfence und _mm_mfence verwenden? Natürlich verwenden Sie Barrieren normalerweise nicht selbst manuell, sondern lassen sie vom Compiler für die Quelle ausgeben, die verwendet wird std::atomic<>.

    – Peter Cordes

    21. Februar 20 um 2:42 Uhr


  • @ user997112: um mehr Leistung als seq_cst zu erhalten, wenn Sie nicht so viel Ordnung benötigen. mov + mfence (oder xchg) ist ziemlich langsam. Erfassen und Freigeben sind zur Laufzeit kostenlos, aber entspannt kann die Kompilierzeit-Optimierung anderer Operationen rund um das Atomic ermöglichen. (Atomic RMW-Operationen auf x86 sind immer eine vollständige Barriere; seq_cst-reine Speicher sind die teure Sache.) Im Allgemeinen sollten Sie für maximale Leistung eine so schwache Bestellung wie unbedingt erforderlich verwenden. Verwenden Sie im Allgemeinen für maximale Sicherheit gegen Entwurfsfehler einfach die Standardeinstellung seq_cst, insbesondere wenn Sie Ihren Code nicht auf einem schwachen ISA testen können.

    – Peter Cordes

    21. Februar 20 um 5:19 Uhr


  • @ user997112: ach. preshing.com/20120515/memory-reordering-in-the-act erwischt. Sie benötigen seq_cst, wenn Sie speichern und dann laden möchten, und sehen, was andere Threads möglicherweise sehen / gesehen haben. Und ja, die Neuordnung zur Kompilierungszeit muss das ISO C++-Speichermodell respektieren (nicht das HW-Speichermodell für Fälle, in denen sie sich unterscheiden, z. B. kann ein gelockerter Speicher zur Kompilierzeit neu geordnet werden, oder ein Erfassungsladevorgang kann nur beim Kompilieren in eine Richtung neu geordnet werden Zeit, relativ zu entspannten und nicht atomaren Operationen. Selbst beim Kompilieren für x86, wo in asm alles eine Erfassungslast ist.)

    – Peter Cordes

    22. Februar 20 um 3:42 Uhr


.

545690cookie-checkC++ Wie wird Release-and-Acquire auf x86 nur mit MOV erreicht?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy