Zmiana kolejności instrukcji odczytu/zapisu w GCC

Prymitywy synchronizacji Linuksa (spinlock, mutex, RCUs) używają instrukcji barrier memory, aby wymusić ponowne uporządkowanie instrukcji dostępu do pamięci. I ta zmiana kolejności może być wykonana albo przez sam procesor, albo przez kompilator.

Czy ktoś może pokazać jakieś przykłady kodu produkowanego przez GCC gdzie taka zmiana kolejności jest wykonywana ? Interesuje mnie głównie x86. Powodem, dla którego pytam, jest zrozumienie, w jaki sposób GCC decyduje, jakie instrukcje można zmienić. Inny x86 architektury mirco (np. sandy bridge vs ivy bridge) używają innej architektury pamięci podręcznej. Dlatego zastanawiam się, w jaki sposób GCC robi skuteczne zmiany kolejności, która pomaga w wydajności wykonania niezależnie od architektury pamięci podręcznej. Bardzo przydatny byłby przykładowy kod C i zmodyfikowany kod generowany przez GCC. Dzięki!

Author: Manohar, 2014-02-28

2 answers

Zmiana kolejności, jaką może wykonać GCC, nie ma związku z zmianą kolejności procesora (x86).

Zacznijmy od zmiany kolejności kompilatora . Reguły języka C są takie, że GCC nie może zmieniać kolejności ładowań volatile i przechowywać dostępu do pamięci względem siebie, lub usuwać je, gdy między nimi występuje punkt sekwencji (Dzięki bobc dla tego wyjaśnienia). Oznacza to, że na wyjściu montażu pojawią się te dostępy do pamięci i będą uporządkowane dokładnie w określonej kolejności. Z drugiej strony dostęp inny niżvolatile może być zmieniony w odniesieniu do wszystkich innych dostępów, volatile lub nie, pod warunkiem, że (zgodnie z zasadą as-if) wynik końcowy obliczeń jest taki sam.

Na przykład, ładowanie Nie - volatile w kodzie C może być wykonane tyle razy, ile kod mówi, ale w innej kolejności (np. jeśli kompilator uważa, że jest wygodniej zrobić to wcześniej lub później, gdy dostępnych jest więcej rejestrów). Można to zrobić mniej razy niż kod mówi (np. jeśli kopia wartości nadal jest dostępna w rejestrze w środku dużego wyrażenia). Lub może być nawet usunięty (np. jeśli kompilator może udowodnić bezużyteczność obciążenia, lub jeśli przeniósł zmienną całkowicie do rejestru).

Aby zapobiec zmianie kolejności kompilatora w innym czasie, musisz użyć specyficznej dla kompilatora bariery. GCC używa do tego celu __asm__ __volatile__("":::"memory");.

Różni się to od CPU reordering , a. k. a. the Model porządkowania pamięci . Starożytne Procesory wykonywały instrukcje dokładnie w kolejności, w jakiej pojawiły się w programie; nazywa się to program ordering, lub strong memory-ordering model. Nowoczesne procesory, jednak czasami uciekają się do "oszustw", aby działać szybciej, przez osłabienie trochę modelu pamięci.

Sposób, w jaki procesory x86 osłabiają model pamięci jest udokumentowany w podręcznikach programistów Intela, tom 3, Rozdział 8, sekcja 8.2.2 " porządkowanie pamięci w P6 i nowsze rodziny procesorów " . Jest to po części to, co czyta:

  • odczyty nie są zmieniane na inne odczyty.
  • zapisy nie są zmieniane na starsze odczyty.
  • zapis do pamięci nie jest zmieniany z innymi zapisami, z [pewnymi] wyjątkami.
  • odczyty mogą być zmieniane ze starszymi zapisami w różnych lokalizacjach, ale nie ze starszymi zapisami w tej samej lokalizacji.
  • odczytu lub zapisu nie można zmienić kolejności za pomocą instrukcji We / Wy, zablokowanych instrukcje lub instrukcje serializujące.
  • Reads nie może przekazać wcześniejszych instrukcji LFENCE i MFENCE.
  • zapis nie może przekazać wcześniejszych instrukcji LFENCE, SFENCE i MFENCE.
  • instrukcje nie mogą przejść wcześniejszych odczytów.
  • instrukcje SFENCE nie mogą przejść wcześniejszych zapisów.
  • instrukcje MFENCE nie mogą przekazać wcześniejszych odczytów lub zapisów.

Podaje również bardzo dobre przykłady tego, co można, a czego nie można zmienić, w sekcji 8.2.3 "przykłady ilustrujące Zasady porządkowania pamięci".

Jak widać, używa się instrukcji ogrodzenia, aby zapobiec niewłaściwemu porządkowaniu dostępu do pamięci przez procesor x86.

Wreszcie, może Cię zainteresować ten link , który idzie do dalszych szczegółów i pochodzi z przykładów montażu, których pragniesz.

 21
Author: Iwillnotexist Idonotexist,
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-05-23 12:32:29
  • reguły języka C są takie, że GCC nie może zmieniać kolejności ładunków lotnych i przechowywać dostępu do pamięci względem siebie lub je usuwać.
To nieprawda i wprowadza w błąd. C spec nie udziela takiej gwarancji. Zobacz Kiedy dostępny jest obiekt zmienny?

Standard zachęca kompilatorów do powstrzymania się od optymalizacji dostępu do obiektów lotnych, ale pozostawia implementację zdefiniowaną jako co stanowi niestabilny dostęp. Minimalnym wymogiem jest to, że w punkcie sekwencji wszystkie poprzednie dostępy do obiektów lotnych ustabilizowały się i nie doszło do kolejnych dostępów. W ten sposób implementacja może dowolnie zmieniać kolejność i łączyć niestabilne dostępy, które występują między punktami sekwencji, ale nie może tego zrobić dla dostępu w całym punkcie sekwencji.

Tradycyjnie Programiści polegali na volatile jako taniej metodzie synchronizacji, ale nie jest to już niezawodna metoda.

 3
Author: bobc,
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
2015-09-12 08:12:35