Der Modifikator __volatile__
an einem __asm__
Block zwingt den Compiler-Optimierer, den Code unverändert auszuführen.Ohne es könnte der Optimierer denken, dass es entweder direkt entfernt oder aus einer Schleife herausgehoben und zwischengespeichert werden kann.
Dies ist nützlich für die rdtsc
Anweisung wie folgt:
__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d))
Dieser Vorgang dauert keine Abhängigkeiten, so dass der Compiler könnte den Wert annehmen kann zwischengespeichert werden. Volatile wird verwendet, um einen neuen Zeitstempel zu lesen.
Wenn allein, so benutzten:
__asm__ __volatile__ ("")
Es wird nicht wirklich etwas auszuführen. Sie können dies erweitern, obwohl, eine Compiler-Speicherbarriere zu erhalten, die keine Anweisungen Speicherzugriff Nachbestellung ermöglicht:
__asm__ __volatile__ ("":::"memory")
Die rdtsc
Anweisung ein gutes Beispiel für die flüchtig ist. rdtsc
wird normalerweise verwendet, wenn Sie wissen möchten, wie lange einige Anweisungen zur Ausführung benötigen. Stellen Sie sich vor, einige Code wie diesen, wo Sie Zeit wollen r1
und r2
‚s Ausführung:
__asm__ ("rdtsc": "=a" (a0), "=d" (d0))
r1 = x1 + y1;
__asm__ ("rdtsc": "=a" (a1), "=d" (d1))
r2 = x2 + y2;
__asm__ ("rdtsc": "=a" (a2), "=d" (d2))
Hier ist der Compiler tatsächlich erlaubt den Zeitstempel cachen und gültige Ausgabe zeigen könnte, dass jede Zeile hat genau 0 Takte auszuführen . Natürlich ist dies nicht das, was Sie wollen, so dass Sie __volatile__
einführen verhindern Caching:
__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))
Jetzt werden Sie einen neuen Zeitstempel jedes Mal, aber es hat immer noch ein Problem, dass sowohl die Compiler und die CPU erlaubt um alle diese Aussagen neu zu ordnen. Es könnte am Ende die asm-Blöcke ausführen, nachdem r1 und r2 bereits berechnet wurden. Um dies zu umgehen, sollten Sie einige Hindernisse hinzufügen, die Serialisierung erzwingen:
__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")
Notiere die mfence
Anweisung hier, die eine CPU-Seite Barriere erzwingt, und das „Gedächtnis“ Bezeichnern in dem flüchtigen Block, der einen Compiler erzwingt Zeitsperre. Bei modernen CPUs können Sie mfence:rdtsc
durch rdtscp
für etwas effizienter ersetzen.
https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html (sonst ignoriere das '__' überall,'__inline__' ist einfach'inline'. –