Co ASM volatile robi w C?
Zajrzałem do jakiegoś kodu C z
Http://www.mcs.anl.gov / ~kazutomo/rdtsc.html
Używają rzeczy typu " inline", "asm " itd jak poniżej:
Kod1:
static __inline__ tick gettick (void) {
unsigned a, d;
__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) );
return (((tick)a) | (((tick)d) << 32));
}
Code2:
volatile int __attribute__((noinline)) foo2 (int a0, int a1) {
__asm__ __volatile__ ("");
}
Zastanawiałem się, co robi code1 i code2?
3 answers
Modyfikator __volatile__
na bloku __asm__
zmusza optymalizator kompilatora do wykonania kodu w stanie takim, w jakim jest. Bez niego optymalizator może myśleć, że można go usunąć bezpośrednio lub wyciągnąć z pętli i buforować.
Jest to przydatne dla rdtsc
instrukcji jak tak:
__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) )
To nie wymaga zależności, więc kompilator może założyć, że wartość może być buforowana. Lotny jest używany, aby zmusić go do odczytania świeżej znacznika czasu.
Gdy używane samodzielnie, tak:
__asm__ __volatile__ ("")
It niczego nie wykona. Można to jednak rozszerzyć, aby uzyskać barierę pamięci w czasie kompilacji, która nie pozwoli na zmianę kolejności instrukcji dostępu do pamięci: {]}
__asm__ __volatile__ ("":::"memory")
Instrukcja rdtsc
jest dobrym przykładem dla lotnych. rdtsc
jest zwykle używany, gdy potrzebujesz czasu, jaki zajmuje wykonanie niektórych instrukcji. Wyobraź sobie taki kod, w którym chcesz wykonać r1
i r2
:
__asm__ ("rdtsc": "=a" (a0), "=d" (d0) )
r1 = x1 + y1;
__asm__ ("rdtsc": "=a" (a1), "=d" (d1) )
r2 = x2 + y2;
__asm__ ("rdtsc": "=a" (a2), "=d" (d2) )
Tutaj kompilator ma prawo buforować znacznik czasu i jest poprawny wyjście może pokazywać, że każda linia wymagała dokładnie 0 zegarów do wykonania. Oczywiście nie jest to to, czego chcesz, więc wprowadzasz __volatile__
, aby zapobiec buforowaniu:
__asm__ __volatile__("rdtsc": "=a" (a0), "=d" (d0))
r1 = x1 + y1;
__asm__ __volatile__("rdtsc": "=a" (a1), "=d" (d1))
r2 = x2 + y2;
__asm__ __volatile__("rdtsc": "=a" (a2), "=d" (d2))
Teraz otrzymasz nowy znacznik czasu za każdym razem, ale nadal ma problem, że zarówno kompilator, jak i procesor mogą zmienić kolejność wszystkich tych instrukcji. Może skończyć się wykonaniem bloków asm po tym, jak r1 i r2 zostały już obliczone. Aby to obejść, dodałbyś pewne bariery, które wymuszają serializacja:
__asm__ __volatile__("mfence;rdtsc": "=a" (a0), "=d" (d0) :: "memory")
r1 = x1 + y1;
__asm__ __volatile__("mfence;rdtsc": "=a" (a1), "=d" (d1) :: "memory")
r2 = x2 + y2;
__asm__ __volatile__("mfence;rdtsc": "=a" (a2), "=d" (d2) :: "memory")
Zwróć uwagę na instrukcję mfence
, która wymusza barierę po stronie CPU, oraz specyfikację "pamięci" w bloku lotnym, która wymusza barierę czasu kompilacji. Na nowoczesnych procesorach możesz zastąpić mfence:rdtsc
rdtscp
czymś bardziej wydajnym.
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-12-12 16:25:17
asm
służy do włączania natywnego kodu asemblera do kodu źródłowego C. Np.
int a = 2;
asm("mov a, 3");
printf("%i", a); // will print 3
Kompilatory mają różne jego warianty. __asm__
powinien być synonimem, może z pewnymi różnicami specyficznymi dla kompilatora.
volatile
oznacza, że zmienna może być modyfikowana z zewnątrz (aka nie przez program C). Na przykład podczas programowania mikrokontrolera, gdzie adres pamięci 0x0000x1234
jest mapowany do interfejsu określonego urządzenia (np. podczas kodowania dla Gameboya, przyciski / screen / etc są / align = "left" / )
volatile std::uint8_t* const button1 = 0x00001111;
Ta wyłączona optymalizacja kompilatora, która opiera się na *button1
nie zmienia się, chyba że zostanie zmieniona przez kod.
Jest również używany w programowaniu wielowątkowym (obecnie już nie jest potrzebny?), gdzie zmienna może być modyfikowana przez inny wątek.
inline
jest wskazówką dla kompilatora do" inline " wywołań funkcji.
inline int f(int a) {
return a + 1
}
int a;
int b = f(a);
To nie powinno być kompilowane do wywołania funkcji f
, ale do int b = a + 1
. Jakby f
gdzie makro. Kompilatory najczęściej optymalizacja ta odbywa się automatycznie w zależności od użycia funkcji/zawartości. __inline__
w tym przykładzie może mieć bardziej konkretne znaczenie.
Podobnie __attribute__((noinline))
(składnia specyficzna dla GCC) zapobiega inlinowaniu funkcji.
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
2014-10-19 23:37:53
Atrybut __asm__
określa nazwę, która ma być użyta w kodzie asemblera dla funkcji lub zmiennej.
Kwalifikator __volatile__
, zwykle używany w obliczeniach w czasie rzeczywistym systemów wbudowanych, rozwiązuje problem z testami kompilatora status register
dla bitu ERROR
lub READY
, powodując problemy podczas optymalizacji. __volatile__
został wprowadzony jako sposób mówienia kompilatorowi, że obiekt podlega szybkiej zmianie i wymusić, aby każde odniesienie obiektu było prawdziwym odniesieniem.
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-10-24 23:23:28