Diese neue Antwort verwendet die Funktion von C ++ 11 <chrono>
. Während es andere Antworten gibt, die die Verwendung zeigen <chrono>
, zeigt keine von ihnen die Verwendung <chrono>
mit der RDTSC
in mehreren der anderen Antworten hier erwähnten Funktion. Also dachte ich , ich würde zeigen , wie die Verwendung RDTSC
mit <chrono>
. Ich werde zusätzlich zeigen , wie Sie den Testcode auf der Uhr templatize können , so dass Sie schnell umschalten können zwischen RDTSC
und Ihrem System eingebaute Uhr Einrichtungen (was wahrscheinlich basierend auf wird clock()
, clock_gettime()
und / oder QueryPerformanceCounter
.
Beachten Sie, dass die RDTSC
Anweisung x86-spezifisch ist. QueryPerformanceCounter
ist nur Windows. Und clock_gettime()
ist nur POSIX. Im Folgenden stelle ich zwei neue Uhren vor: std::chrono::high_resolution_clock
und std::chrono::system_clock
, die, wenn Sie C ++ 11 annehmen können, jetzt plattformübergreifend sind.
Hier erfahren Sie zunächst, wie Sie aus der Intel- rdtsc
Assembly-Anweisung eine C ++ 11-kompatible Uhr erstellen . Ich werde es nennen x::clock
:
#include <chrono>
namespace x
{
struct clock
{
typedef unsigned long long rep;
typedef std::ratio<1, 2'800'000'000> period; // My machine is 2.8 GHz
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<clock> time_point;
static const bool is_steady = true;
static time_point now() noexcept
{
unsigned lo, hi;
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
return time_point(duration(static_cast<rep>(hi) << 32 | lo));
}
};
} // x
Dieser Takt zählt lediglich die CPU-Zyklen und speichert sie in einer vorzeichenlosen 64-Bit-Ganzzahl. Möglicherweise müssen Sie die Assembler-Syntax für Ihren Compiler anpassen. Oder Ihr Compiler bietet eine Eigenart an, die Sie stattdessen verwenden können (z now() {return __rdtsc();}
. B. ).
Um eine Uhr zu erstellen, müssen Sie ihr die Darstellung (Speichertyp) geben. Sie müssen auch die Taktperiode angeben, die eine Kompilierungszeitkonstante sein muss, obwohl Ihr Gerät die Taktrate in verschiedenen Leistungsmodi ändern kann. Und von diesen aus können Sie die "native" Zeitdauer und den Zeitpunkt Ihrer Uhr anhand dieser Grundlagen leicht definieren.
Wenn Sie nur die Anzahl der Tick-Ticks ausgeben möchten, spielt es keine Rolle, welche Anzahl Sie für die Clock-Periode angeben. Diese Konstante kommt nur ins Spiel, wenn Sie die Anzahl der Tick-Ticks in eine Echtzeiteinheit wie Nanosekunden umwandeln möchten. Und in diesem Fall ist die Umrechnung in Nanosekunden (Millisekunden, was auch immer) umso genauer, je genauer Sie die Taktrate angeben können.
Unten finden Sie einen Beispielcode, der die Verwendung zeigt x::clock
. Eigentlich habe ich den Code auf der Uhr als Vorlage verwendet, um zu zeigen, wie Sie viele verschiedene Uhren mit genau derselben Syntax verwenden können. Dieser spezielle Test zeigt, wie hoch der Schleifenaufwand ist, wenn Sie das ausführen, was Sie unter einer Schleife messen möchten:
#include <iostream>
template <class clock>
void
test_empty_loop()
{
// Define real time units
typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
// or:
// typedef std::chrono::nanoseconds nanoseconds;
// Define double-based unit of clock tick
typedef std::chrono::duration<double, typename clock::period> Cycle;
using std::chrono::duration_cast;
const int N = 100000000;
// Do it
auto t0 = clock::now();
for (int j = 0; j < N; ++j)
asm volatile("");
auto t1 = clock::now();
// Get the clock ticks per iteration
auto ticks_per_iter = Cycle(t1-t0)/N;
std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
// Convert to real time units
std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
<< "ps per iteration\n";
}
Als erstes erstellt dieser Code eine "Echtzeit" -Einheit, in der die Ergebnisse angezeigt werden. Ich habe Pikosekunden ausgewählt, aber Sie können beliebige Einheiten auswählen, entweder ganzzahlig oder Gleitkomma. Als Beispiel gibt es eine vorgefertigte std::chrono::nanoseconds
Einheit, die ich hätte verwenden können.
Als weiteres Beispiel möchte ich die durchschnittliche Anzahl von Taktzyklen pro Iteration als Gleitkomma ausdrucken, also erstelle ich eine andere Dauer, basierend auf double, die die gleichen Einheiten hat wie der Tick der Uhr ( Cycle
im Code aufgerufen ).
Die Schleife wird mit Aufrufen clock::now()
auf beiden Seiten zeitgesteuert . Wenn Sie den von dieser Funktion zurückgegebenen Typ benennen möchten, ist dies:
typename clock::time_point t0 = clock::now();
(wie im x::clock
Beispiel deutlich gezeigt , und gilt auch für die vom System gelieferten Uhren).
Um eine Dauer in Form von Gleitkomma-Takt-Ticks zu erhalten, subtrahiert man lediglich die beiden Zeitpunkte, und um den Wert pro Iteration zu erhalten, dividieren Sie diese Dauer durch die Anzahl der Iterationen.
Sie können die Anzahl in beliebiger Dauer mithilfe der count()
Elementfunktion abrufen. Dies gibt die interne Darstellung zurück. Schließlich std::chrono::duration_cast
konvertiere ich die Dauer Cycle
in die Dauer picoseconds
und drucke sie aus.
Die Verwendung dieses Codes ist einfach:
int main()
{
std::cout << "\nUsing rdtsc:\n";
test_empty_loop<x::clock>();
std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
test_empty_loop<std::chrono::high_resolution_clock>();
std::cout << "\nUsing std::chrono::system_clock:\n";
test_empty_loop<std::chrono::system_clock>();
}
Oben übe ich den Test mit unseren hausgemachten x::clock
Uhren aus und vergleiche diese Ergebnisse mit zwei der vom System gelieferten Uhren: std::chrono::high_resolution_clock
und std::chrono::system_clock
. Für mich druckt dies aus:
Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration
Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration
Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration
Dies zeigt, dass jede dieser Uhren eine andere Tick-Periode hat, da die Ticks pro Iteration für jede Uhr sehr unterschiedlich sind. Bei der Umrechnung in eine bekannte Zeiteinheit (z. B. Pikosekunden) erhalte ich jedoch für jede Uhr ungefähr das gleiche Ergebnis (Ihr Kilometerstand kann variieren).
Beachten Sie, dass mein Code völlig frei von "magischen Konvertierungskonstanten" ist. In der Tat gibt es im gesamten Beispiel nur zwei magische Zahlen:
- Die Taktrate meiner Maschine, um zu definieren
x::clock
.
- Die Anzahl der zu testenden Iterationen. Wenn durch Ändern dieser Anzahl Ihre Ergebnisse stark variieren, sollten Sie wahrscheinlich die Anzahl der Iterationen erhöhen oder Ihren Computer während des Testens von konkurrierenden Prozessen entleeren.