Das ist eine bessere Barriere auf x86: lock+Zustellbett oder xchgl?
Den Linux-kernel verwendet lock; addl $0,0(%%esp)
als schreib-Barriere, während die RE2-Bibliothek verwendet xchgl (%0),%0
als schreib-Barriere. Was ist der Unterschied und was ist besser?
Tut x86 erfordern auch Lesen barrier-Anweisungen? RE2 definiert die lese-Barriere-Funktion als no-op auf x86-Linux während es definiert, entweder als lfence
oder no-op, je nachdem, ob SSE2 verfügbar ist. Wann ist lfence
erforderlich?
InformationsquelleAutor Hongli | 2010-11-20
Schreibe einen Kommentar Antworten abbrechen
Du musst angemeldet sein, um einen Kommentar abzugeben.
Den "lock; addl $0,0(%%esp)" wird schneller in den Fall, dass wir testen, der 0-Zustand des lock-variable (%%esp) - Adresse. Weil wir hinzufügen 0-Wert zu lock-variable und das zero-flag ist auf 1 gesetzt, wenn die Sperre Wert der variable an der Adresse (%%esp) 0.
lfence aus dem Intel-Datenblatt:
(Anmerkung der Redaktion:
mfence
oder einelock
ed operation ist nur sinnvoll, Zaun (nach dem speichern) für die sequentielle Konsistenz.lfence
hat nicht block StoreLoad Neuordnung durch den store buffer.)Zum Beispiel: Speicher-schreib-Befehl wie " mov " sind Atomare (die Sie nicht brauchen, lock-Präfix), wenn es ordnungsgemäß ausgerichtet sind. Aber dieser Befehl wird normal ausgeführt CPU-cache und wird nicht Global sichtbar in diesem moment für alle anderen threads, weil Speicher Zaun muss zuerst durchgeführt werden, um diesen thread zu warten, bis die vorherigen Läden sind sichtbar für andere threads.
Also der wesentliche Unterschied zwischen diesen beiden Anweisungen ist, dass xchgl Anweisung hat keine Auswirkungen auf die bedingte flags. Sicherlich können wir testen, die lock-variable state mit lock cmpxchg Auftrag, aber dies ist noch komplexer als bei lock add $0 Unterricht.
lock; addl $0,0(%%esp)
odersfence
muss ich anrufenlfence
im anderen Prozess/thread Lesen, bevor der Speicher? Oder ist das Schloss/sfence instruction selbst schon garantieren, dass andere CPUs die Daten sehen?sfence
aber nicht verwendenlfence
. Brauche ichlock; add
als lese-Hindernis, oder kann ich Weg mit nicht mit einer read-barrier?lfence
ist nie sinnvoll für memory bestellen, es sei denn, Sie Lesen aus dem video-RAM (oder einige andere WC-schwach-geordnete region) mit MOVNTDQA Lasten. Serialisieren von out-of-order execution (aber nicht den Speicher-Puffer) ist nicht sinnvoll, zu stoppen StoreLoad Neuordnung (die einzige Art, die x86 starken memory-Modell ermöglicht die normalen WB (write-back) Speicher-Regionen). Sie brauchen entweder eine verriegelt-Anweisung, xchg-mem, odermfence
(nichtlfence
). Dies ist, warum Menschen nutzenlock addl $0, (%esp)
als stand-alone-Zaun mit keine Nebenwirkungen hat (außer Stress für FAHNEN), wenn Sie nicht bereits tun, alle anderen atomaren RMW ops.mfence
. Dieaddl
ist keine op, denn er fügt die null, die nicht nichts. Diexchg
ist nicht offensichtlich ein no-op auf die kleinen Schnipsel zur Verfügung gestellt, doch wenn man sich in der RE2 Bibliothek, vielleicht ist es eine dummy-Position, oder vielleicht der Wert ist bereits bekannt, enthalten diesen Wert. Ansonsten, die beiden sind gar nicht vergleichbar!xchg
zu tun seq_cst store + Barriere, wie Compiler machen, wie ich beschrieben in meiner Antwort.xchgl (%0),%0
scheint nicht, wie es tun könnte, dass da die gleichen Platzhalter ist auf beiden Seiten. Es tauscht was ist an einer bestimmten Adresse mit dem Wert der Adresse (glaube ich?), das scheint weitgehend nutzlos, außer als dummy-Operationen für die Bestellung von Nebenwirkungen.Zitat aus dem IA32-Handbücher (Vol 3A, Kapitel 8.2: die speicheranordnung):
Hinweis: Die "single-Prozessor-system" oben " ist etwas irreführend. Die gleichen Regeln gelten für jede (logische) Prozessor individuell; das Handbuch beschreibt dann die weitere Bestellung von Regeln zwischen mehreren Prozessoren. Die nur wenig über es in Bezug auf die Frage ist, dass
Kurz gesagt, so lange wie Sie ' re schreiben write-back-Speicher (das ist alle Erinnerung, die Sie jemals sehen, solange du nicht einen Treiber oder Grafik-Programmierer), die meisten x86-Anweisungen werden fast sequentiell konsistent - nur die Neuordnung eine x86 CPU ausführen kann, ist neu anordnen später (unabhängig) liest auszuführen, bevor Sie schreibt. Die wichtigste Sache über das schreiben von Barrieren ist, dass Sie eine
lock
Präfix (implizit oder explizit), die verbietet allen Neuordnung und sorgt dafür, dass die Operationen in der gleichen Reihenfolge von allen Prozessoren in einem multi-Prozessor-system.Ebenfalls in der write-back-Speicher, liest nie nachbestellt, so gibt es keine Notwendigkeit für lese-Barrieren. Aktuelle x86-Prozessoren haben eine schwächere Speicher-Konsistenz-Modell für das streaming speichert und write-combined memory (Häufig verwendet für die zugeordneten Grafikspeicher). Das ist, wo die verschiedenen
fence
Anweisungen ins Spiel kommen; Sie sind nicht notwendig für alle anderen Speicher-Typ, aber einige Treiber in den Linux-kernel viel zu tun mit write-combined memory, so dass Sie nur definiert die schreib-Barriere Weg. Die Liste der Bestellung Modell pro Speicher-Typ ist in Abschnitt 11.3.1, in Vol. 3A der IA-32-Handbücher. Kurze version: Write-Through, Write-Back und Write-Protected zulassen liest (nach den Regeln wie oben beschrieben), Uncachable und Starke Uncacheable Speicher hat starke Bestellung garantiert (keine Prozessor-Neuordnung, liest/schreibt werden sofort ausgeführt, verwendet für MMIO) - und Write-Combined memory schwache Ordnung (d.h. entspannte Bestellung von Regeln, die müssen Zäune).lock addl $0, (%esp)
ist ein Ersatz fürmfence
, nichtlfence
.Der use-case ist, wenn Sie brauchen, um zu blockieren StoreLoad Neuordnung (die einzige Art, die x86 starken memory-Modell ermöglicht), aber Sie brauchen nicht eine Atomare RMW-operation auf einer gemeinsamen Variablen. https://preshing.com/20120515/memory-reordering-caught-in-the-act/
z.B. vorausgesetzt, ausgerichtet
std::atomic<int> a,b
:Ihre Optionen sind:
xchg
, z.B.mov $1, %eax
/xchg %eax, a
so dass Sie nicht brauchen, eine separate Barriere; es ist Teil des Geschäfts. Ich denke, dies ist die effizienteste option auf den meisten modernen hardware; C++11-Compilern anderen als den gcc verwendenxchg
für seq_cst speichert.mfence
als Barriere. (gcc verwendetmov
+mfence
für seq_cst speichert).Verwenden
lock addl $0, (%esp)
als Barriere. Allelock
ed-Belehrung, ist eine vollständige Barriere. Macht lock xchg haben das gleiche Verhalten wie mfence?(Oder an einem anderen Speicherort, aber der Stapel ist fast immer private und heiß in L1d, so ist es ein etwas guter Kandidat. Allerdings könnte auch eine Abhängigkeit der Kette für etwas mit den Daten auf der Unterseite des Stapels.)
Können Sie nur verwenden
xchg
wie eine Barriere, indem man es in ein Geschäft, weil es bedingungslos schreibt der Speicherbereich mit einem Wert, der nicht davon abhängig, wird der alte Wert.Wenn möglich, mit
xchg
für eine seq-cst-store ist wahrscheinlich am besten, obwohl es auch liest aus dem freigegebenen Speicherort.mfence
ist langsamer als erwartet auf die jüngsten Intel-CPUs (Sind lädt und speichert der nur Anweisungen, wird nachbestellt?), auch die Blockierung out-of-order-Ausführung von unabhängigen, nicht-Speicher-Anweisungen auf die gleiche Weiselfence
tut.Könnte es sogar Wert sein, mit
lock addl $0, (%esp)/(%rsp)
stattmfence
auch wennmfence
verfügbar ist, aber ich habe noch nicht experimentiert mit den Schattenseiten. Mit-64(%rsp)
oder so ähnlich könnte es weniger wahrscheinlich, um zu verlängern ein Daten-Abhängigkeit auf etwas heiß ist (eine lokale oder eine Rückkehr-Adresse), aber das können Werkzeuge wie valgrind unglücklich.lfence
ist nie sinnvoll für memory bestellen, es sei denn, Sie Lesen aus dem video-RAM (oder einige andere WC-schwach-geordnete region) mit MOVNTDQA Lasten.Serialisieren out-of-order execution (aber nicht den Speicher-Puffer) ist nicht sinnvoll, zu stoppen StoreLoad Neuordnung (die einzige Art, die x86 starken memory-Modell ermöglicht die normalen WB (write-back) Speicherbereiche).
Den real-world use cases für
lfence
sind für die Blockierung out-of-order-Ausführung vonrdtsc
für das timing sehr kurze code-Blöcke, oder für Spectre-Minderung durch die Blockierung Spekulationen über eine bedingte oder indirekte Zweig.Siehe auch Wann sollte ich _mm_sfence _mm_lfence und _mm_mfence (meine Antwort und @BeeOnRope Antwort) mehr darüber, warum
lfence
ist nicht nützlich, und wenn die Barriere Anweisungen. (Oder in der mine, die C++ - Interna bei der Programmierung in C++ anstelle von asm).Als einer neben den anderen Antworten, die HotSpot-devs gefunden, dass
lock; addl $0,0(%%esp)
mit einem null-offset kann nicht optimal sein, auf einigen Prozessoren kann es einführen falschen Abhängigkeiten; Verwandte jdk bug.Berühren einen Stapel Position mit einem anderen offset kann die Leistung verbessern, unter bestimmten Umständen.
Wichtiger Teil der
lock; addl
undxchgl
ist dielock
Präfix. Es ist implizit, fürxchgl
. Es gibt wirklich keinen Unterschied zwischen den beiden. Ich würde schauen, wie Sie sich versammeln und wählen Sie die eine, die ist kürzer (in bytes), da in der Regel schneller für die entsprechenden Vorgänge auf x86 (also tricks wiexorl eax,eax
)Das Vorhandensein von SSE2 ist wahrscheinlich nur ein proxy für den realen Zustand ist letztlich eine Funktion der
cpuid
. Ist es wahrscheinlich stellt sich heraus, dass SSE2 setzt die Existenz vonlfence
und die Verfügbarkeit von SSE2 wurde geprüft/Cache beim Booten.lfence
ist erforderlich, wenn es verfügbar ist.lfence
ist Teil der SSE2-Befehlssatz. Es ist kein proxy.lfence
ist nicht erforderlich für die Speicher bestellen, es sei denn, du tustmovntdqa
schwach bestellt Lasten von WC-Speicher (z.B. aus video-RAM).mfence
ist eine alternative, volle Barriere, die Sie ersetzen könnteaddl $0, (%esp)
, aberlfence
ist nicht stark genug, um zu stoppen StoreLoad Neuordnung. Sie definitiv nie brauchen beides. (Und BTW,mfence
ist ziemlich langsam und hat einen größeren Einfluss auf die OoO exec alsxchg
oderlock
ed Instruktion auf Intel-CPUs: Sind lädt und speichert der nur Anweisungen, wird nachbestellt?)