GNU/Linux >> LINUX-Kenntnisse >  >> Linux

CPU-Zyklen mit RDTSC erhalten - warum steigt der Wert von RDTSC immer?

Es gibt viele verwirrende und/oder falsche Informationen über den TSC da draußen, also dachte ich, ich versuche, einige davon zu klären.

Als Intel erstmals den TSC (in den ursprünglichen Pentium-CPUs) einführte, war klar dokumentiert, dass er Zyklen (und nicht die Zeit) zählt. Damals liefen CPUs jedoch meistens mit einer festen Frequenz, sodass einige Leute das dokumentierte Verhalten ignorierten und es stattdessen zur Zeitmessung verwendeten (insbesondere Linux-Kernel-Entwickler). Ihr Code brach in späteren CPUs ein, die nicht mit einer festen Frequenz laufen (aufgrund der Energieverwaltung usw.). Zu dieser Zeit waren andere CPU-Hersteller (AMD, Cyrix, Transmeta usw.) verwirrt und einige implementierten TSC, um Zyklen zu messen, und einige implementierten es, um die Zeit zu messen, und einige machten es konfigurierbar (über ein MSR).

Dann wurden "Multi-Chip"-Systeme für Server üblicher; und noch später wurde Multi-Core eingeführt. Dies führte zu geringfügigen Unterschieden zwischen den TSC-Werten auf verschiedenen Kernen (aufgrund unterschiedlicher Startzeiten); aber was noch wichtiger ist, es führte auch zu großen Unterschieden zwischen den TSC-Werten auf verschiedenen CPUs, die dadurch verursacht wurden, dass CPUs mit unterschiedlichen Geschwindigkeiten liefen (aufgrund der Energieverwaltung und/oder anderer Faktoren).

Leute, die versuchten, es von Anfang an falsch zu verwenden (Leute, die es zum Messen von Zeit und nicht von Zyklen verwendeten), beschwerten sich viel und überzeugten schließlich die CPU-Hersteller, den TSC standardmäßig dazu zu bringen, Zeit und nicht Zyklen zu messen.

Das war natürlich ein Durcheinander - z.B. Es braucht viel Code, um festzustellen, was der TSC tatsächlich misst, wenn Sie alle 80x86-CPUs unterstützen. und verschiedene Power-Management-Technologien (einschließlich Dinge wie SpeedStep, aber auch Dinge wie Schlafzustände) können TSC auf verschiedenen CPUs auf unterschiedliche Weise beeinflussen; Daher hat AMD ein "TSC invariant"-Flag in CPUID eingeführt, um dem Betriebssystem mitzuteilen, dass der TSC verwendet werden kann, um die Zeit korrekt zu messen.

Alle neueren Intel- und AMD-CPUs sind schon seit einiger Zeit so - TSC zählt die Zeit und misst überhaupt keine Zyklen. Das bedeutet, wenn Sie Zyklen messen möchten, müssen Sie (modellspezifische) Leistungsüberwachungszähler verwenden. Leider sind die Leistungsüberwachungszähler ein noch schlimmeres Durcheinander (aufgrund ihrer modellspezifischen Beschaffenheit und komplizierten Konfiguration).


Solange Ihr Thread auf demselben CPU-Kern bleibt, gibt der RDTSC-Befehl eine steigende Zahl zurück, bis er umläuft. Bei einer 2-GHz-CPU geschieht dies nach 292 Jahren, es ist also kein wirkliches Problem. Sie werden es wahrscheinlich nicht sehen. Wenn Sie erwarten, so lange zu leben, stellen Sie sicher, dass Ihr Computer neu gestartet wird, sagen wir alle 50 Jahre.

Das Problem mit RDTSC ist, dass Sie keine Garantie haben, dass es auf allen Kernen einer älteren Multicore-CPU zum selben Zeitpunkt startet, und keine Garantie, dass es auf allen CPUs auf einem älteren Multi-CPU-Board zum selben Zeitpunkt startet .
Moderne Systeme haben solche Probleme normalerweise nicht, aber das Problem kann auch auf älteren Systemen umgangen werden, indem die Affinität eines Threads so eingestellt wird, dass er nur auf einer CPU läuft. Dies ist nicht gut für die Anwendungsleistung, also sollte man es im Allgemeinen nicht tun, aber zum Messen von Ticks ist es in Ordnung.

(Ein weiteres "Problem" ist, dass viele Leute RDTSC zur Zeitmessung verwenden, was nicht ist was es tut, aber Sie haben geschrieben, dass Sie CPU-Zyklen wollen, also ist das in Ordnung. Wenn Sie tun Verwenden Sie RDTSC, um die Zeit zu messen, werden Sie möglicherweise überrascht, wenn Energiesparen oder Hyperboost oder wie auch immer die Vielzahl von Frequenzänderungstechniken genannt werden, einsetzt. Für die tatsächliche Zeit, der clock_gettime syscall ist überraschend gut unter Linux.)

Ich würde einfach rdtsc schreiben innerhalb der asm -Anweisung, die für mich gut funktioniert und besser lesbar ist als irgendein obskurer Hex-Code. Angenommen, es ist der richtige Hex-Code (und da er weder abstürzt noch eine ständig steigende Zahl zurückgibt, scheint es so), ist Ihr Code gut.

Wenn Sie die Anzahl der Ticks messen möchten, die ein Codeabschnitt benötigt, benötigen Sie eine Tick-Differenz , müssen Sie nur zwei Werte des ständig ansteigenden Zählers subtrahieren. Etwas wie uint64_t t0 = rdtsc(); ... uint64_t t1 = rdtsc() - t0;
Beachten Sie, dass Sie, wenn sehr genaue, vom umgebenden Code isolierte Messungen erforderlich sind, vor dem Aufruf von rdtsc serialisieren, d. h. die Pipeline anhalten müssen (oder verwenden Sie rdtscp die nur auf neueren Prozessoren unterstützt wird). Die einzige Serialisierungsanweisung, die auf jeder Berechtigungsebene verwendet werden kann, ist cpuid .

Zur weiteren Frage im Kommentar:

Der TSC beginnt bei Null, wenn Sie den Computer einschalten (und das BIOS setzt alle Zähler auf allen CPUs auf denselben Wert zurück, obwohl einige BIOS vor einigen Jahren dies nicht zuverlässig taten).

Aus Sicht Ihres Programms hat der Zähler also "zu einer unbekannten Zeit in der Vergangenheit" begonnen und erhöht sich immer mit jedem Takt, den die CPU sieht. Wenn Sie also die Anweisung ausführen, die diesen Zähler jetzt und später in einem anderen Prozess zurückgibt, wird ein größerer Wert zurückgegeben (es sei denn, die CPU wurde zwischenzeitlich angehalten oder ausgeschaltet). Unterschiedliche Läufe desselben Programms erhalten größere Zahlen, da der Zähler weiter wächst. Immer.

Jetzt clock_gettime(CLOCK_PROCESS_CPUTIME_ID) ist eine andere sache. Dies ist die CPU-Zeit, die das Betriebssystem dem Prozess gegeben hat. Es beginnt bei Null, wenn Ihr Prozess beginnt. Ein neuer Prozess beginnt ebenfalls bei Null. Somit erhalten zwei nacheinander laufende Prozesse sehr ähnliche oder identische Nummern, nicht immer wachsende.

clock_gettime(CLOCK_MONOTONIC_RAW) kommt der Funktionsweise von RDTSC näher (und ist auf einigen älteren Systemen damit implementiert). Es gibt einen Wert zurück, der immer größer wird. Heutzutage ist dies typischerweise ein HPET. Dies ist jedoch wirklich Zeit , und nicht Häkchen . Wenn Ihr Computer in den Energiesparmodus wechselt (z. B. bei 1/2 normaler Frequenz), bleibt er still im gleichen Tempo voranschreiten.


gute Antworten bereits, und Damon hat dies bereits in gewisser Weise in seiner Antwort erwähnt, aber ich werde dies aus dem eigentlichen Eintrag des x86-Handbuchs (Band 2, 4-301) für RDTSC hinzufügen:

Lädt den aktuellen Wert des Zeitstempelzählers des Prozessors (ein 64-Bit-MSR) in die EDX:EAX-Register. Das EDX-Register wird mit den höherwertigen 32 Bits des MSR geladen und das EAX-Register wird mit den niederwertigen 32 Bits geladen. (Auf Prozessoren, die die Intel 64-Architektur unterstützen, werden die höherwertigen 32 Bits von RAX und RDX gelöscht.)

Der Prozessor inkrementiert den Zeitstempelzähler MSR monoton bei jedem Taktzyklus und setzt ihn auf 0 zurück, wenn der Prozessor zurückgesetzt wird. Siehe "Time Stamp Counter" in Kapitel 17 des Intel® 64 and IA-32 Architectures Software Developer's Manual, Band 3B , für spezifische Details zum Verhalten des Zeitstempelzählers.


Linux
  1. Wann haben Sie das letzte Mal Windows verwendet?

  2. Warum benötigt der Root-Benutzer eine Sudo-Berechtigung?

  3. Warum rät die Apt-key-Manpage davon ab, ihren Add-Befehl zu verwenden?

  4. Linux – Der angemessene Wert von Vm.swappiness bei der Verwendung von Zram?

  5. Warum beginnt die Unix-Zeit am 1.1.1970?

Warum jetzt die beste Zeit ist, GNOME zu verwenden

Warum sind bei Shotwell die X- und Y-Positionen umgekehrt, wenn Crop verwendet wird?

Warum geht die Ausgabe einiger Linux-Programme weder an STDOUT noch an STDERR?

Erhöht LVM das Risiko von Datenverlust?

Warum funktioniert das Setuid-Bit inkonsistent?

Wie sind CPU-Zeit und CPU-Auslastung gleich?