Negative clock-cycle-Messungen mit back-to-back rdtsc?
Schreibe ich ein C-code für die Messung der Anzahl von Taktzyklen benötigt, ist der Erwerb einer semaphore. Ich bin mit rdtsc und vor der Messung auf die semaphore, ich nenne rdtsc zwei mal hintereinander zu Messen, der overhead. Ich wiederhole, das viele Male, in einer for-Schleife, und dann benutze ich den Mittelwert als rdtsc overhead.
Ist dies zu korrigieren, verwenden Sie den Mittelwert, erste von allen?
Dennoch, das große problem hier ist, dass manchmal bekomme ich negative Werte für den Aufwand (nicht unbedingt der Durchschnitt,aber-zumindest teilweise-innerhalb der for-Schleife).
Dies wirkt sich auch auf die aufeinander folgende Berechnung der Anzahl der cpu-Zyklen benötigt für die sem_wait()
operation, die manchmal stellt sich auch heraus, negativ zu sein. Wenn das, was ich geschrieben habe, ist nicht klar, hier gibt ' s ein Teil von dem code, den ich auf Arbeit bin.
Warum bin ich immer so negative Werte?
(Anmerkung der Redaktion: siehe Get CPU-Zyklus zählen? für eine korrekte und portable Weg, um die volle 64-bit-Zeitstempel. Ein "=A"
asm-Einschränkung wird nur das low-oder high 32 bits, die bei der Kompilierung für x86-64, je nachdem, ob register allocation passiert zu Holen RAX oder RDX für die uint64_t
Ausgabe. Es wird nicht pick edx:eax
.)
(editor 2. Hinweis: Hoppla, das ist die Antwort, warum wir immer negative Ergebnisse. Noch lohnt sich verlassen eine Notiz hier als eine Warnung, nicht zu kopieren, diese rdtsc
Umsetzung.)
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
static inline uint64_t get_cycles()
{
uint64_t t;
//editor's note: "=A" is unsafe for this in x86-64
__asm volatile ("rdtsc" : "=A"(t));
return t;
}
int num_measures = 10;
int main ()
{
int i, value, res1, res2;
uint64_t c1, c2;
int tsccost, tot, a;
tot=0;
for(i=0; i<num_measures; i++)
{
c1 = get_cycles();
c2 = get_cycles();
tsccost=(int)(c2-c1);
if(tsccost<0)
{
printf("#### ERROR!!! ");
printf("rdtsc took %d clock cycles\n", tsccost);
return 1;
}
tot = tot+tsccost;
}
tsccost=tot/num_measures;
printf("rdtsc takes on average: %d clock cycles\n", tsccost);
return EXIT_SUCCESS;
}
InformationsquelleAutor der Frage Discipulus | 2013-11-12
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn Intel erfand das TSC-es gemessene CPU-Zyklen. Aufgrund der verschiedenen power-management-Funktionen "Zyklen pro Sekunde" ist nicht konstant, so TSC war ursprünglich gut für die Messung der performance von code (und schlecht für die Messung Zeit, die verstrichen ist).
Besser oder schlechter; damals CPUs nicht wirklich zu viel power management, oft CPUs liefen auf einen festen "Zyklen pro Sekunde" sowieso. Einige Programmierer habe die falsche Idee und missbraucht die TSC für die Messung der Zeit und nicht in Zyklen. Später (wenn die Nutzung der power-management-Funktionen häufiger wurde) diese Menschen missbrauchen TSC um die Zeit zu Messen jammerte über all die Probleme, die Ihren Missbrauch verursacht. CPU-Hersteller (beginnend mit AMD) geändert TSC-es misst die Zeit und nicht die Zyklen (so dass es gebrochen für die Messung der performance von code, aber korrekt für die Messung Zeit, die verstrichen ist). Dies verursacht Verwirrung (es war schwer für die software, um festzustellen, was TSC tatsächlich gemessen), so dass ein wenig später auf AMD fügte hinzu, die "TSC Invariant" Flagge zu CPUID, so dass, wenn dieses flag gesetzt ist Programmierer wissen, dass der TSC ist gebrochen (für die Messung der Zyklen) oder fest (für die Zeitmessung).
Intel, gefolgt von AMD und verändert das Verhalten Ihrer TSC auch die Zeit Messen, und nahm auch AMD ' s "TSC Invariant" - flag.
Diese gibt 4 verschiedene Fälle:
TSC Maßnahmen sowohl Zeit als auch Leistung (Zyklen pro Sekunde ist konstant)
TSC misst die performance nicht mal
TSC misst die Zeit und nicht die Leistung, aber nicht die "TSC Invariant" - flag zu sagen, so
TSC misst die Zeit und nicht die Leistung und nicht der "TSC Invariant" - flag, das zu sagen (die meisten modernen CPUs)
Fällen, in denen TSC Maßnahmen Zeit, um die Leistung zu Messen/Zyklen richtig verwenden Sie die performance-überwachung von Leistungsindikatoren. Leider Leistungsüberwachung-Leistungsindikatoren sind für verschiedene CPUs (Modell-spezifisch) und benötigt Zugriff auf die MSRs (privilegierten code). Dies macht es erheblich unpraktisch für Anwendungen zu Messen "Zyklen".
Beachten Sie auch, dass, wenn das TSC hat die Zeit Messen, die Sie nicht wissen können, was Zeit-Skala gibt es (wie viele Nanosekunden in einer "so tun, Zyklus"), ohne mit einer anderen Zeit-Quelle zu bestimmen, wird ein Skalierungsfaktor.
Das zweite problem ist, dass für multi-CPU-Systemen die meisten Betriebssysteme saugen. Der richtige Weg für ein OS zu behandeln, der TSC ist zu verhindern, dass Anwendungen von der Nutzung direkt (durch Einstellung der
TSD
- flag in CR4; so dass der RDTSC-Anweisung löst eine Ausnahme). Dies verhindert, dass diverse Sicherheitslücken (timing side-Kanäle). Es ermöglicht auch, das OS zu emulieren, die TSC und sicherzustellen, dass es gibt ein korrektes Ergebnis. Zum Beispiel, wenn eine Anwendung verwendet, die RDTSC-Instruktion und eine Ausnahme ausgelöst wird, die OS-exception-handler herausfinden können, eine richtige "Globale Zeitstempel", um zurückzukehren.Natürlich verschiedene CPUs haben Ihre eigenen TSC. Dies bedeutet, dass, wenn eine Anwendung verwendet, TSC-direkt bekommen Sie unterschiedliche Werte auf unterschiedlichen CPUs. Menschen zu helfen, arbeiten rund um die OS Versagen, das problem zu beheben (durch die Emulation RDTSC, wie es sein sollte); AMD Hinzugefügt, die
RDTSCP
Anweisung gibt die TSC und ein "Prozessor-ID" (Intel endete die Verabschiedung derRDTSCP
Anleitung zu). Eine Anwendung läuft auf einem kaputten OS können Sie die "Prozessor-ID" zu erkennen, wenn Sie ausgeführt werden, die auf eine andere CPU aus der letzten Zeit; und in dieser Weise (über dieRDTSCP
Anleitung), können Sie wissen, Wann "elapsed = TSC - previous_TSC" enthält ein gültiges Ergebnis. Allerdings, das "Prozessor-ID" zurückgegeben, indem diese Anleitung ist nur ein Wert in einer MSR -, und das OS hat diesen Wert legen Sie auf jede CPU etwas anders ist - sonstRDTSCP
sagen, dass die "Prozessor-ID" ist von null auf allen CPUs.Grundsätzlich; wenn die CPUs unterstützt, die
RDTSCP
Instruktion, und wenn der OS ist richtig eingestellt, die "Prozessor-ID" (mit dem MSR); dann dieRDTSCP
Unterricht kann helfen, Anwendungen wissen, wenn Sie haben eine schlechte "verstrichene Zeit" Ergebnis (aber es nicht sowieso zu fixieren und zu vermeiden das schlechte Ergebnis).So, um zu schneiden eine lange Geschichte kurz, wenn Sie möchten, eine genaue performance-Messung, sind Sie meistens aufgeschmissen. Die besten kann man realistischerweise hoffen, dass für eine genaue Zeitmessung, aber nur in einigen Fällen (z.B. beim laufen auf einem single-CPU-Maschine oder "angeheftet", um eine bestimmte CPU; oder bei der Verwendung von
RDTSCP
auf OSs, dass es sich richtig, solange Sie erkennen und verwerfen ungültige Werte).Natürlich auch dann erhalten Sie von zwielichtigen Messungen wegen der Dinge, die wie IRQs. Aus diesem Grund, es ist am besten zum ausführen von code viele Male in einer Schleife und entsorgen Sie alle Ergebnisse, die sind auch viel höher als die anderen Ergebnisse.
Schließlich, wenn Sie wirklich wollen, um es richtig zu machen, sollten Sie Messen den Aufwand der Messung. Um dies zu tun würden Sie Messen, wie lange es dauert, nichts zu tun (nur die RDTSC/RDTSCP Einweisung alleine, beim verwerfen dodgy Messungen); subtrahieren Sie dann den Aufwand der Messung von der "Messung etwas" Ergebnisse. Dies gibt Ihnen eine bessere Schätzung der Zeit "etwas" tatsächlich nimmt.
Hinweis: Wenn Sie Graben, bis eine Kopie der Intel-System Programming Guide aus, wenn der Pentium-zuerst veröffentlicht wurde (Mitte der 1990er Jahre - nicht sicher, ob es mehr im Netz verfügbar - ich habe archivierte Kopien seit den 1980er Jahren), wirst du feststellen, dass Intel dokumentiert die time stamp counter als etwas, das "kann verwendet werden, zu überwachen und zu identifizieren, die relative Zeit der Entstehung der Prozessor Veranstaltungen". Sie garantiert, dass (ausgenommen 64-bit-wrap-around) es würde monoton steigen (aber nicht, dass Sie würde zu einer Erhöhung bei einem fixed-rate) und die, die es machen würde, mindestens 10 Jahre, bevor es verpackt herum. Die neueste revision dieses Handbuches dokumentiert die time stamp counter mit mehr Details, die besagt, dass für ältere CPUs (P6, Pentium M, ältere Pentium 4) der time stamp counter "inkrementiert mit jeder internen Prozessor-Takt-Zyklus" und die "Intel(r) SpeedStep(r) technology übergänge kann Auswirkungen auf die Prozessor-Uhr"; und dass neuere CPUs (neuere Pentium 4, Core Solo, Core Duo, Core 2, Atom) die TSC-Schritten mit einer Konstanten rate (und das ist die "architektonische Verhalten moving forward"). Im wesentlichen, es von Anfang an war eine (variable) "interne cycle-counter" verwendet werden für einen Zeit-Stempel (und nicht ein Zeit-Zähler verwendet werden, um track "wall clock" - Zeit), und dieses Verhalten änderte sich bald nach dem Jahr 2000 (basierend auf Pentium-4-release-Datum).
InformationsquelleAutor der Antwort Brendan
nicht verwenden avg-Wert
Verwenden Sie die kleinste oder avg kleinere Werte statt (avg bekommen, weil der CACHE ist), weil die größeren wurde unterbrochen von OS multi-tasking.
Könnte man auch daran denken, alle Werte und dann festgestellt, dass die OS-Prozess Granularität Grenze und filter alle Werte nach dieser Grenze (in der Regel >
1ms
die leicht nachweisbar)keine Notwendigkeit zu Messen, overhead
RDTSC
Du nur Messen offseted von einiger Zeit und dem gleichen offset in beiden Zeiten und nach dem Abzug ist es Weg.
für variable-clock-source von
RDTS
(wie auf laptops)Sollten Sie ändern Sie die Geschwindigkeit der CPU seine max, von einigen stetige intensive Berechnungs-Schleife in der Regel wenige Sekunden sind genug. Sie sollten Messen die CPU Frequenz kontinuierlich und beginnen, Messen Sie Ihre Sache nur, wenn es stabil genug ist.
InformationsquelleAutor der Antwort Spektre
Wenn Sie den code startet auf einem Prozessor dann-swaps zu anderen, die timestamp-Differenz kann negativ sein aufgrund Prozessoren schlafen usw.
Versuchen Sie, die Prozessor-Affinität bevor Sie die Messung beginnen.
Ich kann nicht sehen, ob Sie unter Windows oder Linux von der Frage, so werde ich die Antwort für beide.
Windows:
Linux:
InformationsquelleAutor der Antwort Neil
Die anderen Antworten sind toll (lies Sie), aber davon ausgehen, dass
rdtsc
wird richtig gelesen. Diese Antwort ist es, die inline-asm-Fehler, führt zu völlig falschen Ergebnissen, einschließlich der negativen.Die andere Möglichkeit ist, dass du die Erstellung dieser als 32-bit-code, aber mit viel mehr wiederholt, und bekam eine gelegentliche negative Intervall auf CPU-migration auf ein system, das nicht invariant-TSC (synchronisiert TSCs über alle Kerne). Entweder ein multi-sockel-system, oder eine ältere multi-core. CPU-TSC-fetch-Vorgang-besonders in multicore-multi-Prozessor-Umgebung.
Wenn Sie kompilieren für x86-64, Ihre negativen Ergebnisse ausführlich erläutert werden, die durch Ihre falsche
"=A"
output Einschränkung fürasm
. Sehen Get CPU-Zyklus zählen? auf korrekte Weise zu verwenden, rdtsc, sind tragbar zu allen Compilern und 32 vs. 64-bit-Modus. Oder verwenden Sie"=a"
und"=d"
Ausgänge und ignorieren Sie einfach die Obere Hälfte ausgegeben, für kurze Intervalle, die nicht überlaufen 32 bits.)(Ich bin überrascht Sie nicht erwähnt, dass Sie auch riesige und Wild-Variation, sowie überlaufende
tot
zu geben, eine negative Durchschnittliche, auch wenn keine einzelnen Messungen waren negativ. Ich sehe Durchschnitte wie-63421899
oder69374170
oder115365476
.)Kompilieren mit
gcc -O3 -m32
macht es funktionieren wie erwartet, Druck-Mittelwerte von 24 bis 26 (falls es in einer Schleife, so dass die CPU bleibt bei top-speed, sonst wie 125 Referenz-Zyklen für die 24 core clock Zyklen zwischen back-to-back -rdtsc
auf Skylake). https://agner.org/optimize/ für den Unterricht Tabellen.Asm details, was falsch gelaufen ist mit der
"=A"
Einschränkungrdtsc
(insn ref manuelle Eingabe) immer produziert, die die zwei 32-bit -hi:lo
Hälften seiner 64-bit-Ergebnis inedx:eax
auch in 64-bit-Modus, wo wir wirklich lieber in einem einzelnen 64-bit-register.Sie erwarten, dass die
"=A"
Ausgabe-Einschränkung zu Holenedx:eax
füruint64_t t
. Aber das ist nicht das, was passiert. Für eine variable, die passt in eine registrieren die compiler-picks entwederRAX
oderRDX
und übernimmt die andere ist unverändertwie ein"=r"
constraint auswählt, registrieren und übernimmt den rest unverändert. Oder ein"=Q"
Einschränkung wählt eine von a,b,c, oder d.... (Siehe x86-Einschränkungen).In x86-64, Sie würde in der Regel nur wollen
"=A"
für eineunsigned __int128
operand, wie ein mehrere Ergebnis-oderdiv
Eingang. Es ist eine Art hack, da mit%0
im asm-template erweitert nur um das Tiefe register, und es gibt keine Warnung, wenn"=A"
nicht Verwendung sowohla
undd
registriert.Genau zu sehen, wie diese verursacht ein problem, ich habe einen Kommentar innerhalb des asm-template:
__asm__ volatile ("rdtsc # compiler picked %0" : "=A"(t));
. So können wir sehen, was der compiler erwartet, basierend auf dem, was wir gesagt haben, es mit Operanden.Die resultierende Schleife (in Intel-syntax) sieht so aus, kompilieren eine gereinigte version des Codes auf der Godbolt compiler explorer für 64-bit-gcc und 32-bit-clang:
Wenn der compiler die Berechnung
c2-c1
es eigentlich Berechnunghi-lo
aus dem 2.rdtsc
,weil wir uns belogen der compiler über das, was die asm-Anweisung nicht. Der 2.rdtsc
clobberedc1
Wir ihm gesagt, dass er hatte die Wahl, auf welche register die Ausgabe, so wird es abgeholt, ein register der ersten Zeit, und die andere das 2. mal, so dass Sie nicht brauchen
mov
Anweisungen.Die TSC zählt Referenz-Zyklen seit dem letzten Neustart. Aber der code hängt nicht von
hi<lo
es kommt nur darauf an, die Zeichen derhi-lo
. Dalo
wickelt sich um jede Sekunde oder zwei (2^32 Hz ist in der Nähe zu 4.3 GHz), läuft das Programm zu einer gegebenen Zeit ist etwa eine 50% chance zu sehen, ein negatives Ergebnis.Es hängt nicht von den aktuellen Wert von
hi
; es gibt vielleicht 1 Teil in2^32
Neigung in die eine oder in die andere Richtung, weilhi
änderungen durch, wennlo
umschlingt.Seit
hi-lo
ist ein fast gleichmäßig verteilt auf 32-bit-integer-überlauf, der Durchschnitt ist sehr gemeinsam. Dein code ist ok, wenn der Durchschnitt ist normalerweise klein. (Aber siehe andere Antworten dafür, warum wollen Sie nicht das bedeuten, Sie wollen, median oder etwas zum ausschließen von Ausreißern.)InformationsquelleAutor der Antwort Peter Cordes
Den wichtigsten Punkt meiner Frage war nicht die Genauigkeit des Ergebnisses, sondern die Tatsache, dass ich immer negative Werte jedes jetzt und dann (ersten Aufruf rdstc gibt größeren Wert als zweiten Anruf).
Tun mehr Forschung (und das Lesen anderer Fragen auf dieser website), fand ich heraus, dass eine Art und Weise für immer Dinge, die die Arbeit, wenn Sie rdtsc ist, um eine cpuid-Befehl, bevor Sie es. Dieser Befehl serialisiert den code. Dies ist, wie ich die Dinge jetzt:
Ich bin noch immer eine NEGATIVE Differenz zwischen dem zweiten Anruf und Erster Aufruf der get_cycles Funktion. WARUM? Ich bin nicht 100% sicher über die syntax, die von der cpuid-Montage von inline-code, das ist, was ich gefunden auf der Suche im internet.
InformationsquelleAutor der Antwort Discipulus
In das Gesicht von Wärme-und idle-Drosselung, die Maus-Bewegung und den Netzwerk-Verkehr unterbricht, was immer es tut, mit der GPU, und all den anderen Aufwand, ein modernes multicore-system aufnehmen kann, ohne dass jemand zu viele sorgen, ich denke deine einzige vernünftige Kurs für dieses ist, sammeln Sie ein paar tausend einzelnen Proben und werfen Sie nur die Ausreißer vor der Einnahme von median oder Mittelwert (kein Statistiker, aber ich werde-venture-es wird nicht viel Unterschied hier).
Ich würde denken, was Sie tun, um zu beseitigen den Lärm eines Laufenden Systems wird die Ergebnisse verzerren viel schlimmer, als einfach zu akzeptieren, dass es keine Weise, die Sie jemals in der Lage sein, um zuverlässig vorherzusagen, wie lange es dauert alles haben, um diese Tage.
InformationsquelleAutor der Antwort jthill
rdtsc kann verwendet werden, um eine zuverlässige und sehr präzise verstrichene Zeit. Wenn Sie linux verwenden, können Sie sehen, ob Ihr Prozessor unterstützt eine Konstante rate tsc durch einen Blick in /proc/cpuinfo, um zu sehen, wenn Sie constant_tsc definiert.
Stellen Sie sicher, dass Sie bleiben auf dem gleichen Kern. Jeder Kern hat seinen eigenen tsc hat seinen eigenen Wert. Verwenden rdtsc stellen Sie sicher, dass Sie entweder tasksetoder SetThreadAffinityMask (windows) oder pthread_setaffinity_npum sicherzustellen, dass Ihr Prozess bleibt auf dem gleichen Kern.
Dann teilen Sie dies von Ihrem Haupt-Taktrate, die auf linux finden Sie in /proc/cpuinfo oder Sie können dies zur Laufzeit durch
rdtsc
clock_gettime
schlafen für 1 Sekunde
clock_gettime
rdtsc
dann sehen, wie viele ticks pro Sekunde, und dann teilen Sie den Unterschied in Zecken, um herauszufinden, wie viel Zeit verstrichen ist.
InformationsquelleAutor der Antwort Michael
Wenn der thread, der ist mit den code bewegt sich zwischen den Kernen, dann ist es möglich, dass die rdtsc-Wert zurückgegeben, der kleiner als der Wert Lesen, auf dem anderen core. Der Kern der nicht alle den Zähler auf 0 zu genau der selben Zeit, wenn das Paket Kräfte. So stellen Sie sicher, dass Sie thread-Affinität zu einem bestimmten Kern, wenn Sie Ihren Testfall ausgeführt.
InformationsquelleAutor der Antwort BitTwiddler
Getestet habe ich Ihren code auf meinem Rechner und ich dachte mir, dass während der RDTSC-fuction nur uint32_t zumutbar ist.
Ich den folgenden Code in meinen code zu korrigieren:
InformationsquelleAutor der Antwort Zhu Guoliang