AtomicInteger lazySet vs. set

116

Jaka jest różnica między metodami lazySeti ? Dokumentacji nie ma wiele do powiedzenia na temat :setAtomicIntegerlazySet

Ostatecznie ustawia się na podaną wartość.

Wydaje się, że przechowywana wartość nie zostanie natychmiast ustawiona na żądaną wartość, ale zamiast tego zostanie zaplanowana do ustawienia w przyszłości. Ale jakie jest praktyczne zastosowanie tej metody? Jakiś przykład?

Cheok Yan Cheng
źródło

Odpowiedzi:

114

Cytowane bezpośrednio z „JDK-6275329: Dodaj metody lazySet do klas atomowych” :

Jako prawdopodobnie ostatnia kontynuacja JSR166 dla Mustanga, dodaliśmy metodę „lazySet” do klas Atomic (AtomicInteger, AtomicReference itp.). Jest to niszowa metoda, która jest czasami przydatna podczas dostrajania kodu przy użyciu nieblokujących struktur danych. Semantyka polega na tym, że zapis nie zostanie ponownie uporządkowany z żadnym poprzednim zapisem, ale może zostać zmieniony w kolejnych operacjach (lub, równoważnie, może nie być widoczny dla innych wątków), dopóki nie nastąpi inna nietrwała akcja zapisu lub synchronizacji).

Głównym przypadkiem użycia jest zerowanie pól węzłów w nieblokujących strukturach danych wyłącznie w celu uniknięcia długoterminowego przechowywania elementów bezużytecznych; ma zastosowanie, gdy jest nieszkodliwe, jeśli inne wątki widzą przez jakiś czas wartości inne niż null, ale chcesz się upewnić, że ostatecznie struktury będą GCable. W takich przypadkach można uzyskać lepszą wydajność, unikając kosztów zerowego zapisu ulotnego. Istnieje kilka innych przypadków użycia wzdłuż tych linii dla atomów nie opartych na referencjach, więc metoda jest obsługiwana we wszystkich klasach AtomicX.

Dla osób, które lubią myśleć o tych operacjach w kategoriach barier na poziomie maszyny na zwykłych wieloprocesorach, lazySet zapewnia poprzednią barierę sklep-sklep (która jest albo bez operacji lub bardzo tania na obecnych platformach), ale nie ma bariery dla obciążenia sklepu (co jest zwykle kosztowną częścią zapisu ulotnego).

ziewać
źródło
14
Czy ktoś mógłby to ogłupić dla reszty z nas? :(
Gaurav
14
Leniwa jest wersją nieulotną (np. Zmiana stanu nie jest gwarantowana dla wszystkich wątków, które mają Atomic*zakres).
ziewać
63
nie rozumiem tylko, dlaczego javadoc jest taki kiepski.
Felipe
8
Jestem pewien, że w końcu dojdą do zmiany. Bum Bum.
MMJZ
3
dla tych, którzy chcą dowiedzieć się więcej o barierze sklepowej / załadunkowej i dlaczego bariera sklepowo-sklepowa jest tańsza niż bariera sklepowo-ładunkowa. Oto łatwy do zrozumienia artykuł na ten temat. Mechanical-sympathy.blogspot.com/2011/07/…
Kin Cheung,
15

lazySet może być używany do komunikacji między wątkami rmw, ponieważ xchg jest atomowy, jeśli chodzi o widoczność, gdy proces wątku piszącego modyfikuje lokalizację linii pamięci podręcznej, procesor wątku czytnika zobaczy to przy następnym odczycie, ponieważ protokół spójności pamięci podręcznej procesora Intel zagwarantuje LazySet działa, ale linia pamięci podręcznej zostanie zaktualizowana przy następnym odczycie, ponownie procesor musi być wystarczająco nowoczesny.

http://sc.tamu.edu/systems/eos/nehalem.pdf W przypadku Nehalem, który jest platformą wieloprocesorową, procesory mają możliwość „podsłuchiwania” magistrali adresowej w celu uzyskania dostępu innych procesorów do pamięci systemowej i do ich wewnętrznych pamięci podręcznych. Korzystają z tej możliwości szpiegowania, aby zachować zgodność swoich wewnętrznych pamięci podręcznych zarówno z pamięcią systemową, jak iz pamięcią podręczną innych połączonych ze sobą procesorów. Jeśli podczas podsłuchiwania jeden procesor wykryje, że inny procesor zamierza zapisywać dane w lokalizacji pamięci, która jest obecnie przechowywana w pamięci podręcznej w stanie współdzielonym, procesor szpiegujący unieważni swój blok pamięci podręcznej, zmuszając go do wypełnienia linii pamięci podręcznej przy następnym dostępie do tej samej lokalizacji pamięci .

oracle hotspot jdk dla architektury procesorów x86->

lazySet == unsafe.putOrderedLong == xchg rw (instrukcja asm, która służy jako miękka bariera, kosztująca 20 cykli na procesorze nehelem intel)

na x86 (x86_64) taka bariera jest znacznie tańsza pod względem wydajności niż lotna czy AtomicLong getAndAdd,

W scenariuszu z jednym producentem i jednym konsumentem w kolejce, miękka bariera xchg może wymusić wiersz kodów przed lazySet (sekwencja + 1) dla wątku producenta PRZED jakimkolwiek kodem wątku konsumenckiego, który będzie oczywiście zużywał (pracował) nowe dane Wątek konsumenta będzie musiał niepodzielnie sprawdzić, czy sekwencja producenta została zwiększona o dokładnie jeden przy użyciu funkcji compareAndSet (sekwencja, sekwencja + 1).

Prześledziłem po kodzie źródłowym Hotspot, aby znaleźć dokładne mapowanie lazySet do kodu cpp: http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe. cpp Unsafe_setOrderedLong -> SET_FIELD_VOLATILE definicja -> OrderAccess: release_store_fence. W przypadku x86_64 OrderAccess: release_store_fence jest zdefiniowany przy użyciu instrukcji xchg.

Możesz zobaczyć, jak to jest dokładnie zdefiniowane w jdk7 (doug lea pracuje nad nowymi rzeczami dla JDK 8): http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/4fc084dac61e/src/os_cpu/ linux_x86 / vm / orderAccess_linux_x86.inline.hpp

możesz również użyć hdis do dezasemblacji kodu lazySet w akcji.

Jest jeszcze jedno powiązane pytanie: czy potrzebujemy mfence podczas korzystania z xchg

kotlet schabowy
źródło
5
Trudno zrozumieć, do czego zmierzasz. Czy możesz wyjaśnić swój punkt widzenia?
Paul Bellora
3
"lazySet == unsafe.putOrderedLong == xchg rw (instrukcja asm, która służy jako miękka bariera kosztująca 20 cykli na procesorze nehelem intel) na x86 (x86_64) taka bariera jest znacznie tańsza pod względem wydajności niż lotna lub AtomicLong getAndAdd" -> Nie jest to prawdą, o ile wiem. lazySet / putOrdered to plik MOV do adresu, dlatego książka kucharska JMM opisuje go jako brak operacji na x86.
Nitsan Wakart
11

Szerszą dyskusję na temat pochodzenia i użyteczności lazySet i bazowego putOrdered można znaleźć tutaj: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html

Podsumowując: lazySet jest słabym, zmiennym zapisem w tym sensie, że działa jako sklep, a nie jako ogrodzenie sklepu. Sprowadza się to do tego, że lazySet jest skompilowany w JIT do instrukcji MOV, której kompilator nie może zmienić kolejności, a nie do znacznie droższej instrukcji używanej dla zbioru nietrwałego.

Podczas odczytywania wartości zawsze kończy się to nietrwałym odczytem (w każdym przypadku z Atomic * .get ()).

lazySet oferuje jednemu piszącemu spójny, zmienny mechanizm zapisu, tj. jest całkowicie uzasadnione, aby pojedynczy pisarz używał lazySet do zwiększania licznika, wiele wątków zwiększających ten sam licznik będzie musiało rozwiązać konkurencyjne zapisy przy użyciu CAS, co jest dokładnie tym, co dzieje się w okładki Atomic * for incAndGet.

Nitsan Wakart
źródło
dokładnie, dlaczego nie możemy powiedzieć, że jest to prosta StoreStorebariera, ale nieStoreLoad ?
Eugene
8

Z podsumowania pakietu Concurrent-atomic

lazySet ma efekty pamięciowe pisania (przypisywania) zmiennej nietrwałej, z wyjątkiem tego, że pozwala na zmianę kolejności z kolejnymi (ale nie poprzednimi) działaniami pamięci, które same nie nakładają ograniczeń zmiany kolejności przy zwykłych zapisach nieulotnych. Wśród innych kontekstów użycia lazySet może mieć zastosowanie podczas zerowania, w celu wyrzucania elementów bezużytecznych, odwołania, do którego nigdy więcej nie uzyskano dostępu.

Jeśli ciekawi Cię lazySet, to jesteś winien również inne wyjaśnienia

Efekty pamięci przy dostępach i aktualizacjach atomics generalnie są zgodne z regułami dotyczącymi składników lotnych, jak określono w sekcji 17.4 specyfikacji języka Java ™.

get ma efekt pamięciowy odczytywania zmiennej ulotnej.

set ma na pamięć skutki zapisu (przypisania) zmiennej ulotnej.

lazySet ma efekty pamięciowe pisania (przypisywania) zmiennej nietrwałej, z wyjątkiem tego, że pozwala na zmianę kolejności z kolejnymi (ale nie poprzednimi) działaniami pamięci, które same nie nakładają ograniczeń zmiany kolejności przy zwykłych zapisach nieulotnych. Wśród innych kontekstów użycia lazySet może mieć zastosowanie podczas zerowania, w celu wyrzucania elementów bezużytecznych, odwołania, do którego nigdy więcej nie uzyskano dostępu.

słabeCompareAndSet niepodzielnie odczytuje i warunkowo zapisuje zmienną, ale nie tworzy żadnych uporządkowań zdarzeń przed, więc nie zapewnia żadnych gwarancji w odniesieniu do poprzednich lub kolejnych odczytów i zapisów jakichkolwiek zmiennych innych niż obiekt docelowy słabyCompareAndSet.

compareAndSet i wszystkie inne operacje odczytu i aktualizacji, takie jak getAndIncrement, mają wpływ na pamięć zarówno odczytu, jak i zapisu zmiennych ulotnych.

Ajeet Ganga
źródło
4

Oto moje zrozumienie, popraw mnie, jeśli się mylę: Możesz myśleć o lazySet()"pół" niestabilnym: jest to w zasadzie nieulotna zmienna w sensie czytania przez inne wątki, tj. Wartość ustawiona przez lazySet może być niewidoczna dla innych wątki. Ale staje się niestabilny, gdy występuje inna operacja zapisu (może pochodzić z innych wątków). Jedyny wpływ lazySet, jaki mogę sobie wyobrazić, to compareAndSet. Więc jeśli używasz lazySet(), get()z innych wątków może nadal pobierać starą wartość, ale compareAndSet()zawsze będzie miała nową wartość, ponieważ jest to operacja zapisu.

jyluo
źródło
1
nie masz na myśli compareAndSet?
Dave Moten
2

Re: spróbuj go stłumić -

Można o tym myśleć jako o sposobie traktowania zmiennego pola tak, jakby nie było ulotne dla określonej operacji magazynu (np .: ref = null;).

To nie jest do końca dokładne, ale powinno wystarczyć, abyś mógł zdecydować między „OK, naprawdę mnie to nie obchodzi” a „Hmm, pozwól, że pomyślę o tym przez chwilę”.

Paul Mclachlan
źródło