AtomicInteger lazySet vs. set

116

Was ist der Unterschied zwischen lazySetund setMethoden von AtomicInteger? Die Dokumentation hat nicht viel zu sagen lazySet:

Setzt schließlich auf den angegebenen Wert.

Es scheint, dass der gespeicherte Wert nicht sofort auf den gewünschten Wert gesetzt wird, sondern so geplant wird, dass er irgendwann in der Zukunft eingestellt wird. Aber was ist der praktische Nutzen dieser Methode? Irgendein Beispiel?

Cheok Yan Cheng
quelle

Antworten:

114

Zitiert direkt aus "JDK-6275329: Hinzufügen von LazySet-Methoden zu Atomklassen" :

Als wahrscheinlich letzte kleine JSR166-Nachfolge für Mustang haben wir den Atomic-Klassen (AtomicInteger, AtomicReference usw.) eine "lazySet" -Methode hinzugefügt. Dies ist eine Nischenmethode, die manchmal nützlich ist, wenn Code mithilfe nicht blockierender Datenstrukturen optimiert wird. Die Semantik besteht darin, dass der Schreibvorgang garantiert nicht bei einem vorherigen Schreibvorgang neu angeordnet wird, sondern bei nachfolgenden Vorgängen neu angeordnet werden kann (oder für andere Threads entsprechend nicht sichtbar ist), bis eine andere flüchtige Schreib- oder Synchronisierungsaktion auftritt.

Der Hauptanwendungsfall besteht darin, Felder von Knoten in nicht blockierenden Datenstrukturen auf Null zu setzen, um eine langfristige Speicherbereinigung zu vermeiden. Dies gilt, wenn es harmlos ist, wenn andere Threads für eine Weile Nicht-Null-Werte sehen. Sie möchten jedoch sicherstellen, dass Strukturen schließlich GC-fähig sind. In solchen Fällen können Sie eine bessere Leistung erzielen, indem Sie die Kosten für das Null-Volatile-Write vermeiden. Es gibt auch einige andere Anwendungsfälle in diesem Sinne für nicht referenzbasierte Atomics, sodass die Methode in allen AtomicX-Klassen unterstützt wird.

Für Leute, die diese Vorgänge gerne als Barrieren auf Maschinenebene auf gängigen Multiprozessoren betrachten, bietet lazySet eine vorhergehende Store-Store-Barriere (die auf aktuellen Plattformen entweder nicht verfügbar oder sehr billig ist), aber keine Store-Load-Barriere (Dies ist normalerweise der teure Teil eines flüchtigen Schreibvorgangs).

gähnen
quelle
14
Könnte es jemand für den Rest von uns dumm machen? :(
Gaurav
14
Lazy ist die nichtflüchtige Version (z. B. ist nicht garantiert, dass die Statusänderung für alle Threads sichtbar ist, die den Atomic*Gültigkeitsbereich haben).
Gähnen
63
Was ich nicht verstehe ist, warum Javadoc so arm daran ist.
Felipe
8
Ich bin sicher, dass sie es irgendwann ändern werden. Boom Boom.
MMJZ
3
Für diejenigen, die mehr über die Laden- / Ladungsbarriere erfahren möchten und wissen möchten, warum die Ladenbarriere billiger ist als die Ladenladebarriere. Hier ist ein leicht verständlicher Artikel darüber. mechanisch-sympathy.blogspot.com/2011/07/…
Kin Cheung
15

lazySet kann für die rmw-Kommunikation zwischen Threads verwendet werden, da xchg atomar ist. Wenn der Writer-Thread-Prozess eine Cache-Zeilenposition ändert, wird dies vom Prozessor des Reader-Threads beim nächsten Lesen angezeigt, da das Cache-Kohärenzprotokoll der Intel-CPU dies garantiert LazySet funktioniert, aber die Cache-Zeile wird beim nächsten Lesen aktualisiert. Auch hier muss die CPU modern genug sein.

http://sc.tamu.edu/systems/eos/nehalem.pdf Für Nehalem, eine Multiprozessor-Plattform, haben die Prozessoren die Möglichkeit, den Adressbus für die Zugriffe anderer Prozessoren auf den Systemspeicher und zu überwachen (zu belauschen) zu ihren internen Caches. Sie verwenden diese Snooping-Fähigkeit, um ihre internen Caches sowohl mit dem Systemspeicher als auch mit den Caches in anderen miteinander verbundenen Prozessoren konsistent zu halten. Wenn ein Prozessor durch Snooping erkennt, dass ein anderer Prozessor beabsichtigt, an einen Speicherort zu schreiben, den er derzeit im freigegebenen Zustand zwischengespeichert hat, macht der Snooping-Prozessor seinen Cache-Block ungültig und zwingt ihn, beim nächsten Zugriff auf denselben Speicherort eine Cache-Zeilenfüllung durchzuführen .

Oracle Hotspot JDK für x86-CPU-Architektur->

lazySet == unsafe.putOrderedLong == xchg rw (asm-Anweisung, die als weiche Barriere dient und 20 Zyklen auf nehelem intel cpu kostet)

Auf x86 (x86_64) ist eine solche Barriere in Bezug auf die Leistung viel billiger als flüchtig oder AtomicLong getAndAdd.

In einem Szenario mit einem Produzenten und einer Konsumentenwarteschlange kann xchg soft barriere die Codezeile vor dem lazySet (Sequenz + 1) erzwingen, damit der Produzententhread ausgeführt wird, BEVOR jeder Konsumententhreadcode die neuen Daten verbraucht (bearbeitet) Der Consumer-Thread muss atomar überprüfen, ob die Producer-Sequenz mithilfe eines compareAndSet (Sequenz, Sequenz + 1) um genau eins erhöht wurde.

Ich habe nach dem Hotspot-Quellcode gesucht, um die genaue Zuordnung des lazySet zum cpp-Code zu finden: http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe. cpp Unsafe_setOrderedLong -> SET_FIELD_VOLATILE-Definition -> OrderAccess: release_store_fence. Für x86_64 wird OrderAccess: release_store_fence so definiert, dass die Anweisung xchg verwendet wird.

Sie können sehen, wie es in jdk7 genau definiert ist (Doug Lea arbeitet an einigen neuen Dingen für JDK 8): http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/4fc084dac61e/src/os_cpu/ linux_x86 / vm / orderAccess_linux_x86.inline.hpp

Sie können die hdis auch verwenden, um die Assembly des lazySet-Codes in Aktion zu zerlegen.

Es gibt noch eine andere verwandte Frage: Brauchen wir mfence, wenn wir xchg verwenden?

Schweinekotelett
quelle
5
Es ist schwer zu verstehen, was Sie hier vorhaben. Können Sie bitte Ihren Standpunkt klarstellen?
Paul Bellora
3
"lazySet == unsafe.putOrderedLong == xchg rw (asm-Anweisung, die als weiche Barriere dient und 20 Zyklen auf nehelem intel cpu kostet) auf x86 (x86_64) eine solche Barriere ist in Bezug auf die Leistung viel billiger als flüchtig oder AtomicLong getAndAdd" -> Dies trifft meines Wissens nicht zu. lazySet / putOrdered ist ein MOV zu einer Adresse, weshalb das JMM-Kochbuch es als No-Op auf x86 beschreibt.
Nitsan Wakart
11

Eine breitere Diskussion über die Ursprünge und den Nutzen von lazySet und dem zugrunde liegenden putOrdered finden Sie hier: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html

Zusammenfassend: lazySet ist ein schwaches flüchtiges Schreiben in dem Sinne, dass es als Store-Store und nicht als Store-Load-Zaun fungiert. Dies läuft darauf hinaus, dass lazySet JIT zu einem MOV-Befehl kompiliert wird, der vom Compiler nicht neu geordnet werden kann, anstatt zu dem wesentlich teureren Befehl, der für einen flüchtigen Satz verwendet wird.

Wenn Sie den Wert lesen, führen Sie am Ende immer einen flüchtigen Lesevorgang durch (auf jeden Fall mit einem Atomic * .get ()).

lazySet bietet einem einzelnen Writer einen konsistenten flüchtigen Schreibmechanismus, dh es ist absolut legitim, wenn ein einzelner Writer lazySet zum Inkrementieren eines Zählers verwendet. Mehrere Threads, die denselben Zähler inkrementieren, müssen die konkurrierenden Schreibvorgänge mit CAS auflösen. Genau das geschieht unter die Cover von Atomic * für incAndGet.

Nitsan Wakart
quelle
genau, warum können wir nicht sagen , dass dies eine einfache StoreStoreBarriere, aber nicht ein StoreLoad?
Eugene
8

Aus der Zusammenfassung des Concurrent-Atomic-Pakets

lazySet hat die Speichereffekte des Schreibens (Zuweisens) einer flüchtigen Variablen, außer dass es Neuordnungen mit nachfolgenden (aber nicht vorherigen) Speicheraktionen ermöglicht, die selbst keine Neuordnungsbeschränkungen für gewöhnliche nichtflüchtige Schreibvorgänge auferlegen. Unter anderen Verwendungskontexten kann lazySet angewendet werden, wenn aus Gründen der Speicherbereinigung eine Referenz auf Null gesetzt wird, auf die nie wieder zugegriffen wird.

Wenn Sie neugierig auf lazySet sind, schulden Sie sich auch andere Erklärungen

Die Speichereffekte für Zugriffe und Aktualisierungen von Atomics folgen im Allgemeinen den Regeln für flüchtige Stoffe, wie in Abschnitt 17.4 der Java ™ -Sprachspezifikation angegeben.

get hat die Memory-Effekte des Lesens einer flüchtigen Variablen.

set hat die Speichereffekte des Schreibens (Zuweisens) einer flüchtigen Variablen.

lazySet hat die Speichereffekte des Schreibens (Zuweisens) einer flüchtigen Variablen, außer dass es Neuordnungen mit nachfolgenden (aber nicht vorherigen) Speicheraktionen ermöglicht, die selbst keine Neuordnungsbeschränkungen für gewöhnliche nichtflüchtige Schreibvorgänge auferlegen. Unter anderen Verwendungskontexten kann lazySet angewendet werden, wenn aus Gründen der Speicherbereinigung eine Referenz auf Null gesetzt wird, auf die nie wieder zugegriffen wird.

schwachCompareAndSet liest eine Variable atomar und schreibt sie bedingt, erstellt jedoch keine Vorbestellungen und bietet daher keine Garantie für vorherige oder nachfolgende Lese- und Schreibvorgänge anderer Variablen als des Ziels des schwachCompareAndSet.

compareAndSet und alle anderen Lese- und Aktualisierungsvorgänge wie getAndIncrement haben die Speichereffekte beim Lesen und Schreiben flüchtiger Variablen.

Ajeet Ganga
quelle
4

Hier ist mein Verständnis, korrigieren Sie mich, wenn ich falsch liege: Sie können sich lazySet()als "halbflüchtig" vorstellen: Es ist im Grunde eine nichtflüchtige Variable in Bezug auf das Lesen durch andere Threads, dh der von lazySet festgelegte Wert ist für andere möglicherweise nicht sichtbar Fäden. Es wird jedoch flüchtig, wenn eine andere Schreiboperation auftritt (möglicherweise von anderen Threads). Die einzige Auswirkung von lazySet, die ich mir vorstellen kann, ist compareAndSet. Wenn Sie also verwenden lazySet(), erhalten get()andere Threads möglicherweise immer noch den alten Wert, haben aber compareAndSet()immer den neuen Wert, da es sich um eine Schreiboperation handelt.

Jyluo
quelle
1
Meinst du nicht compareAndSet?
Dave Moten
2

Re: Versuch es zu dumm -

Sie können sich dies als eine Möglichkeit vorstellen, ein flüchtiges Feld so zu behandeln, als wäre es für eine bestimmte Speicheroperation (z. B. ref = null;) nicht flüchtig.

Das ist nicht ganz richtig, aber es sollte ausreichen, dass Sie eine Entscheidung zwischen "OK, es ist mir wirklich egal" und "Hmm, lassen Sie mich ein bisschen darüber nachdenken" treffen können.

Paul Mclachlan
quelle