Co oznacza każda kolejność pamięci?
Przeczytałam rozdział i nie spodobał mi się zbytnio. Nadal Nie wiem, jakie są różnice między poszczególnymi porządkami pamięci. To moje obecne spekulacje, które zrozumiałem po przeczytaniu znacznie prostszego http://en.cppreference.com/w/cpp/atomic/memory_order
Poniżej jest źle, więc nie próbuj się z tego uczyć
- memory_order_relaxed: nie synchronizuje się, ale nie jest ignorowany, gdy kolejność jest wykonywana z innego trybu w innym atomie var
- memory_order_consume: synchronizuje odczyt tej atomowej zmiennej, jednak nie synchronizuje wcześniej napisanych zmiennych. Jednakże jeśli wątek używa var X podczas modyfikowania Y (i zwalnia go). Inne wątki zużywające Y też zobaczą x? Nie wiem, czy to oznacza, że ten wątek wypycha zmiany X (i oczywiście y)
- memory_order_acquire: synchronizuje odczyt tej atomowej zmiennej i upewnia się, że luźne zmienne zapisane przed tą zmienną są również synchronizowane. (czy to oznacza wszystkie atomowe zmienne we wszystkich wątkach są synchronizowane?)
- memory_order_release: przesuwa atomic store do innych wątków (ale tylko jeśli odczytują var z consume/acquire)
- memory_order_acq_rel: dla operacji odczytu/zapisu. Czy nabyć więc nie modyfikować starej wartości i zwalnia zmiany.
- memory_order_seq_cst: to samo co acquire release, z tym, że wymusza wyświetlanie aktualizacji w innych wątkach (if
a
store with release on another thread. Przechowujęb
z seq_cst. A 3. czytanie wątkua
z relax zobaczy zmiany wraz zb
i jakąkolwiek inną zmienną atomową?).
Myślę, że rozumiem, ale popraw mnie, jeśli się mylę. Nie mogłem znaleźć niczego, co wyjaśniałoby to w łatwym do odczytania języku angielskim.
2 answers
GCC Wiki daje bardzo dokładne i łatwe do zrozumienia wyjaśnienie z przykładami kodu.
(fragment zredagowany i podkreślenie dodane)
Ważne:
Po ponownym przeczytaniu poniższego cytatu skopiowanego z GCC Wiki w procesie dodawania własnego sformułowania do odpowiedzi, zauważyłem, że cytat jest rzeczywiście zły. Mają nabyć i skonsumować dokładnie w złym kierunku. Operacja release-consumption zapewnia jedynie zamówienie gwarancja na dane zależne podczas gdy operacja release-acquire zapewnia tę gwarancję niezależnie od tego, czy dane są zależne od wartości atomowej, czy nie.
Pierwszy model jest "spójny sekwencyjnie". Jest to domyślny tryb używany, gdy nie jest określony żaden i jest najbardziej restrykcyjny. Może być również jawnie określony przez
memory_order_seq_cst
. Zapewnia te same ograniczenia i ograniczenia do przenoszenia obciążeń wokół, które programiści sekwencyjni są z natury zaznajomieni z, z tym, że dotyczy to wszystkich wątków .
[...]
Z praktycznego punktu widzenia oznacza to wszystkie operacje atomowe działające jako bariery optymalizacyjne. Można zmieniać porządek między operacjami atomowymi, ale nie między operacjami. Lokalne rzeczy wątku również nie mają wpływu, ponieważ nie ma widoczności dla innych wątków. [...] Ten tryb zapewnia również spójność pomiędzy wszystkimi wątkami.Przeciwne podejście to
memory_order_relaxed
. Model ten pozwala na znacznie mniej Synchronizacja poprzez usunięcie ograniczeń happens-before. Tego typu operacje atomowe mogą mieć również różne optymalizacje wykonywane na nich, takie jak usuwanie martwych magazynów i commoning. [...] Bez żadnych zdarzeń-przed krawędziami żaden wątek nie może liczyć na konkretne zamówienie z innego wątku.
Tryb relaxed jest najczęściej używany, gdy programista po prostu chce, aby zmienna miała charakter atomowy, zamiast używać jej do synchronizacji wątków dla innej pamięci współdzielonej data.Trzeci tryb (
memory_order_acquire
/memory_order_release
) jest hybrydą pomiędzy dwoma pozostałymi. Tryb acquire / release jest podobny do trybu sekwencyjnie spójnego, z tym że stosuje tylko relację happens-before do zmiennych zależnych . Pozwala to na rozluźnienie synchronizacji wymaganej między niezależnymi odczytami i niezależnymi zapisami.
memory_order_consume
jest kolejnym subtelnym udoskonaleniem modelu release / acquire memory, który nieco rozluźnia wymagania poprzez usunięcie happensów przed zleceniem na niezależnych współdzielonych zmiennych, jak również .
[...]
Prawdziwa różnica sprowadza się do tego, ile stanu sprzęt musi spłukać, aby zsynchronizować. Ponieważ operacja consume Może działać szybciej, ktoś, kto wie, co robi, może użyć jej do zastosowań o krytycznym znaczeniu.
Oto moja własna próba bardziej przyziemnego wyjaśnienia:
Inne podejście do tego jest spojrzenie na problem z punktu widzenia zmiany kolejności odczytów i zapisów, zarówno atomowych, jak i zwykłych: {]}
Wszystkie operacje atomowe są gwarantowane jako atomowe same w sobie (kombinacja dwóch operacji atomowych nie jest atomowa jako całość!) i być widoczne w całkowitej kolejności, w jakiej pojawiają się na osi czasu strumienia realizacji. Oznacza to, że żadna operacja atomowa nie może być pod żadnym pozorem zmieniona, ale inne operacje pamięci mogą być bardzo dobrze be. Kompilatory (i procesory) rutynowo dokonują takiej zmiany kolejności jako optymalizacji.
Oznacza to również, że kompilator musi użyć wszelkich instrukcji niezbędnych do zagwarantowania, że operacja atomowa wykonująca się w dowolnym momencie zobaczy wyniki każdej innej operacji atomowej, ewentualnie na innym rdzeniu procesora (ale niekoniecznie innych operacjach), które zostały wykonane wcześniej.
Teraz, zrelaksowany to tylko to, absolutne minimum. Nie robi nic poza tym i nie zapewnia innych Gwarancje. Jest to najtańsza możliwa operacja. W przypadku operacji nie-read-modify-write na silnie uporządkowanych architekturach procesorów (np. x86/amd64)sprowadza się to do zwykłego, zwykłego ruchu.
Operacja sekwencyjnie spójna jest dokładnie odwrotna, wymusza ścisłe porządkowanie nie tylko operacji atomowych, ale także innych operacji pamięci, które mają miejsce przed lub po. Żaden z nich nie może przekroczyć bariery narzuconej przez operację atomową. Praktycznie to oznacza utracone możliwości optymalizacji, a być może trzeba będzie wstawić instrukcje ogrodzenia. Jest to najdroższy model.
OperacjaArelease zapobiega zmianie kolejności zwykłych ładunków i magazynówpo operacji atomowej, podczas gdy operacja acquirezapobiega zmianie kolejności zwykłych ładunków i magazynów przed operacją atomową. Wszystko inne można nadal przenosić.
Połączenie zapobiegania powstawaniu sklepów przesunięte po, a ładunki przenoszone przed odpowiednią operacją atomową zapewniają, że cokolwiek wątek przejmujący widzi jest spójne, z tylko niewielką ilością możliwości optymalizacji utraconych.
Można o tym myśleć jako o czymś w rodzaju nieistniejącego zamka, który jest uwalniany (przez pisarza) i nabywany (przez czytelnika). Z wyjątkiem... nie ma zamka.
W praktyce, release / acquire zazwyczaj oznacza, że kompilator nie musi używać żadnych szczególnie drogich instrukcji specjalnych, ale nie może dowolnie zmieniać kolejności załadowań i magazynów według własnych upodobań, co może przeoczyć pewne (małe) możliwości optymalizacji.
Wreszcie, consumption jest tą samą operacją co acquire, tylko z tym wyjątkiem, że gwarancje zamawiania mają zastosowanie tylko do danych zależnych. Dane zależne będą np. danymi wskazywanymi przez atomicznie zmodyfikowany wskaźnik.
Prawdopodobnie, może to zapewnić kilka możliwości optymalizacji, które nie są obecne w operacje akwizycji (ponieważ mniej danych podlega ograniczeniom), jednak dzieje się to kosztem bardziej złożonego i podatnego na błędy kodu oraz nietrywialnego zadania poprawienia łańcuchów zależności.
Obecnie odradza się stosowanie Consumer zamawianie w trakcie zmiany specyfikacji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-05 21:52:49
To dość złożony temat. Spróbuj przeczytać http://en.cppreference.com/w/cpp/atomic/memory_order kilka razy, spróbuj przeczytać inne zasoby, itp.
Oto uproszczony opis:
Kompilatori CPU mogą zmieniać kolejność dostępu do pamięci. Oznacza to, że mogą się one zdarzyć w innej kolejności niż określona w kodzie. To jest w porządku przez większość czasu, problem pojawia się, gdy różne wątki próbują się komunikować i może zobaczyć taką kolejność dostępu do pamięci to łamie niezmienniki Kodeksu.
Zazwyczaj można użyć blokad do synchronizacji. Problem w tym, że są powolne. Operacje atomowe są znacznie szybsze, ponieważ synchronizacja odbywa się na poziomie CPU (tzn. CPU zapewnia, że żaden inny wątek, nawet na innym CPU, nie modyfikuje jakiejś zmiennej itp. ).
Więc jedynym problemem, z którym mamy do czynienia, jest zmiana kolejności dostępu do pamięci.memory_order
enum określa, jakie typy kompilatora reorderingów muszą zabronić.
relaxed
- bez ograniczeń.
consume
- żadne obciążenia zależne od nowo załadowanej wartości nie mogą być ponownie uporządkowane wrt. ładunek atomowy. Tzn. jeśli są one po ładunku atomowego w kodzie źródłowym, to dzieje się również po ładunku atomowym.
acquire
- nie można zmienić kolejności obciążeń wrt. ładunek atomowy. Tzn. jeśli są one po ładunku atomowego w kodzie źródłowym, to dzieje się również po ładunku atomowym.
release
- nie można zmienić kolejności sklepów wrt. sklep atomowy. Oznacza to, że jeśli są one przed Atomic store w kodzie źródłowym, to stanie się przed Atomic store też.
acq_rel
- acquire
i release
razem.
seq_cst
- trudniej jest zrozumieć, dlaczego to zamówienie jest wymagane. Zasadniczo wszystkie inne porządki zapewniają tylko, że określone niedozwolone zmiany kolejności nie zdarzają się tylko dla wątków, które zużywają/uwalniają tę samą zmienną atomową. Dostęp do pamięci może nadal rozprzestrzeniać się na inne wątki w dowolnej kolejności. To zamówienie zapewnia, że tak się nie stanie (a więc Sekwencyjna spójność). W przypadku, gdy jest to potrzebne, zobacz przykład na końcu połączonej strony.