Frage

Die Linux-Kernel verwendet lock; addl $0,0(%%esp) als Schreibsperre, während die RE2-Bibliothek verwendet als Schreibsperre xchgl (%0),%0. Was ist der Unterschied und was ist besser?

Does x86 benötigen auch Lesesperranweisungen? RE2 definiert seine Lesebarrierefunktion als ein no-op auf x86 Linux während es entweder als lfence definiert oder No-op je nachdem, ob SSE2 verfügbar ist. Wenn lfence erforderlich?

War es hilfreich?

Lösung

Die " Sperre; addl 0,0 $ (%% esp) " ist schneller im Fall, dass wir den 0 Zustand des Sperrvariable an (%% esp) -Adresse zu testen. Weil wir 0 hinzufügen Wert Sperrvariable und der Null-Flag auf 1 gesetzt wird, wenn die Sperre Wert der Variablen bei der Adresse (%% esp) 0 ist.


lfence von Intel Datenblatt:

  

führt eine Serialisierung Betrieb auf   Alle Last-aus-Speicher Anweisungen, die   wurden vor dem LFENCE ausgegeben   Anweisung. diese Serialisierung   Betrieb garantiert, dass jede Last   Anweisung, die in Programm vorausgeht   um die für LFENCE Anweisung   global sichtbar vor jeder Belastung   Anweisung, die die LFENCE folgt   Anweisung ist global sichtbar.

( Anmerkung der Redaktion: mfence oder eine locked Operation ist die einzige nützliche Zaun (nach einem Geschäft) für die sequentielle Konsistenz . lfence tut nicht Block StoreLoad durch den Speicherpuffer Nachbestellung.)


Zum Beispiel: Speicher-Schreib-Befehl wie ‚mov‘ sind atomar (sie keine Sperre Präfix müssen), wenn es richtig ausgerichtet sind. Aber diese Anweisung wird normalerweise in CPU-Cache ausgeführt und wird nicht global in diesem Moment für alle anderen Threads sichtbar sein, weil der Speicher Zaun muss zuerst ausgeführt werden, um diesen Thread warten zu lassen, bis vorherige Geschäfte zu anderen Threads sichtbar sind.


Der Hauptunterschied zwischen diesen beiden Anweisungen ist, dass xchgl Anweisung wird keine Auswirkungen auf den bedingten Flags hat. Sicherlich können wir den Sperrvariable Zustand mit testen Sperre cmpxchg Anweisung, aber das ist noch komplexer als bei Sperre plus $ 0 Anleitung.

Andere Tipps

Zitiert aus den IA32-Handbuch (Vol 3A, Kapitel 8.2: Speicher Reihenfolge):

  

In einem Ein-Prozessor-System für Speicherbereiche definiert als Write-Back-Cache gespeichert werden, die Speicherordnungsmodell gelten die folgenden Grundsätze [..]

     
      
  • Reads nicht mit anderen neu geordnet liest
  •   
  • Writes sind nicht mit neu geordnet älter liest
  •   
  • Writes in dem Speicher nicht mit anderen schreiben neu geordnet, mit Ausnahme von   
        
    • schreibt mit dem CLFLUSH Befehl ausgeführt
    •   
    • Streaming speichert (schreibt) ausgeführt mit den nicht-zeitlichen Bewegungsanweisungen ([Liste der Anweisungen hier])
    •   
    • String-Operationen (siehe Abschnitt 8.2.4.1)
    •   
  •   
  • Reads kann mit älteren Schreibungen in verschiedenen Orten neu geordnet werden, aber nicht mit älteren schreibt an der gleichen Stelle.
  •   
  • Lesen oder Schreiben kann nicht mit E / A-Anweisungen, gesperrt Anweisungen oder Serialisierung Anweisungen
  • neu geordnet werden   
  • Liest nicht LFENCE und MFENCE Anweisungen
  • geben   
  • Writes nicht SFENCE und MFENCE Anweisungen
  • geben   

Hinweis: Die „In einem Ein-Prozessor-System“ über etwas irreführend ist. Die gleichen Regeln gelten für jeden (logischen) Prozessor einzeln; das Handbuch geht dann auf, um die zusätzliche Ordnungsregeln zwischen mehreren Prozessoren zu beschreiben. Das einzige, wenig über sie auf die Frage betreffend ist, dass

  
      
  • Locked Anweisungen haben insgesamt Ordnung.
  •   

Kurz gesagt, solange Sie schreiben auf Write-Back-Speicher (das ist der gesamte Speicher Sie jemals so lange sehen werden, da Sie nicht ein Fahrer oder Grafik-Programmierer sind), die meisten x86-Befehle sind fast sequentiell konsistent - das einzige Neuordnungs eines x86-CPU durchführen kann, ist Neuordnungs später (unabhängig), bevor schreiben auszuführen liest. Die Hauptsache über die Schreibbarrieren ist, dass sie einen lock Präfix (implizit oder explizit) haben, die alle Umordnung und sorgt verbietet, dass die Operationen von allen Prozessoren in einem Multiprozessorsystem in der gleichen Reihenfolge zu sehen sind.

Auch in Write-Back-Speicher, liest nie neu geordnet, so gibt es keine Notwendigkeit für die Lesesperre. Neueste x86-Prozessoren haben ein schwächeres Speicherkonsistenzmodell für das Streaming von Geschäften und Schreib kombinierten Speicher (allgemein für abgebildet Grafikspeicher verwendet wird). Das ist, wo die verschiedenen fence Anweisungen ins Spiel kommen; sie sind nicht für einen anderen Speichertyp notwendig, aber einige Treiber im Linux-Kernel mit Schreib kombinierten Speicher viel zu tun, so dass sie nur auf diese Weise ihre Lese-Barriere definiert. Die Liste der Ordnungsmodell pro Speichertyp ist in Abschnitt 11.3.1 in Vol. 3A der IA-32-Handbuch. Kurzversion: Write-Through, Write-Back und Write-Protected erlauben spekulative liest (nach den Regeln wie oben beschrieben), uncachable und Strong Uncacheable Speicher starke Ordnung garantiert hat (kein Prozessor Neuordnen, liest / schreibt sofort ausgeführt werden, verwendet für MMIO ) und schreiben Kombinierter Speicher hat schwache Ordnung (dh entspannt Ordnungsregeln, dass Bedarf Zaun).

lock addl $0, (%esp) ist ein Ersatz für mfence, nicht lfence.

Die Use-Case ist, wenn Sie benötigen StoreLoad Neuordnen (die einzige Art, dass x86 starke Speichermodell erlaubt) zu blockieren, aber Sie brauchen nicht ein Atom RMW-Operation auf einem gemeinsam genutzten Variable. https://preshing.com/20120515/memory-reordering-caught- in-the-act /

z. ausgerichtet std::atomic<int> a,b Annahme:

movl   $1, a             a = 1;    Atomic for aligned a
# barrier needed here
movl   b, %eax           tmp = b;  Atomic for aligned b

Ihre Optionen sind:

  • Führen Sie eine sequentielle Konsistenz Speicher mit xchg , z.B. mov $1, %eax / xchg %eax, a, so dass Sie keine separate Schranke müssen; es ist Teil des Ladens. Ich denke, das ist die effizienteste Option auf den meisten modernen Hardware; C ++ Compiler 11 andere als gcc Verwendung xchg für seq_cst speichert.
  • mfence Verwendung als Barriere. (Gcc Anwendungen mov + mfence für seq_cst Speicher).
  • Verwenden lock addl $0, (%esp) als Barriere. Jede locked Anweisung ist eine vollständige Barriere. sperrt Xchg das gleiche Verhalten wie mfence? haben

    (Oder an einen anderen Ort, aber der Stapel ist fast immer privat und heiß in L1d, so dass es ein etwas guter Kandidat ist. Doch dies eine Abhängigkeitskette für etwas schaffen kann, die Daten an der Unterseite des Stapels verwendet wird.)

Sie können nur xchg als Barriere verwenden, indem sie in einen Laden Falten, weil sie bedingungslos den Speicherplatz mit einem Wert schreibt, der nicht auf den alten Wert abhängig ist.

Wenn möglich, mit xchg für einen Seq-cst Speicher ist wahrscheinlich am besten, obwohl es auch aus dem gemeinsamen Standort liest. mfence ist langsamer als erwartet auf den letzten Intel-CPUs ( Wann soll ich _mm_sfence verwenden _mm_lfence und _mm_mfence (meine Antwort und @ BeeOnRope Antwort) für mehr darüber, warum lfence ist nicht sinnvoll, und wenn jede der Barriere Anweisungen. (Oder in mir, die C ++ Spezifika bei der Programmierung in C ++ statt asm).

Als Nebenwirkung auf die anderen Antworten fanden die HotSpot Devs, dass lock; addl $0,0(%%esp) mit einer Nullpunktverschiebung kann nicht optimal sein, auf einigen Prozessoren kann es einzuführen falsche Datenabhängigkeiten ; Verwandte jdk bug .

Berühren eine Stack-Position mit einem anderen Offset-Leistung unter bestimmten Umständen verbessern.

Der wichtige Teil von lock; addl und xchgl ist der lock Präfix. Es ist implizit für xchgl. Es gibt wirklich keinen Unterschied zwischen den beiden. Ich würde schauen, wie sie zusammenzubauen und das wählen, das kürzer ist (in Bytes), da dies in der Regel schneller für äquivalente Operationen auf x86 (daher Tricks wie xorl eax,eax)

Die Anwesenheit von SSE2 ist wahrscheinlich nur ein Proxy für den wirklichen Zustand, das letztlich eine Funktion der cpuid ist. Es stellt sich heraus, dass wahrscheinlich SSE2 die Existenz von lfence und die Verfügbarkeit von SSE2 impliziert wurde geprüft / beim Booten im Cache gespeichert. lfence erforderlich ist, wenn es verfügbar ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top