Je souhaite calculer le temps nécessaire à une API pour renvoyer une valeur. Le temps nécessaire à une telle action est de quelques nanosecondes. Comme l’API est une classe/fonction C++, j’utilise le timer.h pour calculer la même chose:
#include <ctime>
#include <cstdio>
using namespace std;
int main(int argc, char** argv) {
clock_t start;
double diff;
start = clock();
diff = ( std::clock() - start ) / (double)CLOCKS_PER_SEC;
cout<<"printf: "<< diff <<'\n';
return 0;
}
Le code ci-dessus donne le temps en secondes. Comment obtenir la même chose en nano secondes et avec plus de précision?
Ce que d'autres ont posté sur l'exécution répétée de la fonction dans une boucle est correct.
Pour Linux (et BSD), vous voulez utiliser clock_gettime () .
#include <sys/time.h>
int main()
{
timespec ts;
// clock_gettime(CLOCK_MONOTONIC, &ts); // Works on FreeBSD
clock_gettime(CLOCK_REALTIME, &ts); // Works on Linux
}
Pour les fenêtres que vous souhaitez utiliser QueryPerformanceCounter . Et voici plus sur QPC
Apparemment, il y a un problème avec QPC connu sur certains chipsets, vous voudrez peut-être vous assurer que vous n'en avez pas. De plus, certains systèmes AMD à double cœur peuvent également causer un problème . Voir le deuxième post de sebbbi, où il déclare:
QueryPerformanceCounter () et QueryPerformanceFrequency () offrent une résolution un peu meilleure, mais présentent des problèmes différents. Par exemple, sous Windows XP, tous les processeurs double cœur AMD Athlon X2 renvoient le PC de l'un des cœurs de manière "aléatoire" (le PC recule parfois un peu en arrière), sauf si vous installez spécialement un package de pilote AMD double cœur pour résoudre le problème. Nous n'avons pas remarqué de processeur dual dual + core ayant des problèmes similaires (p4 dual, p4 ht, core2 dual, core2 quad, phenom quad).
EDIT 2013/07/16:
Il semble y avoir une controverse sur l'efficacité de QPC dans certaines circonstances, comme indiqué dans http://msdn.Microsoft.com/en-us/library/windows/desktop/ee417693 (v = vs.85) .aspx
... Alors que QueryPerformanceCounter et QueryPerformanceFrequency s'adaptent généralement à plusieurs processeurs, des bogues dans le BIOS ou les pilotes peuvent avoir pour conséquence que ces routines renvoient des valeurs différentes lorsque le thread passe d'un processeur à un autre ...
Cependant, cette réponse StackOverflow https://stackoverflow.com/a/4588605/34329 indique que QPC devrait fonctionner correctement sur tout système d’exploitation sous Windows après Win XP service pack 2.
Cet article montre que Windows 7 peut déterminer si le ou les processeurs ont un TSC invariant et s’appuie sur un minuteur externe s’ils ne le font pas. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html La synchronisation entre les processeurs reste un problème.
Autre lecture fine liée aux minuteries:
Voir les commentaires pour plus de détails.
Cette nouvelle réponse utilise la fonctionnalité <chrono>
De C++ 11. Bien qu'il existe d'autres réponses qui montrent comment utiliser <chrono>
, Aucune d'elles ne montre comment utiliser <chrono>
Avec la fonction RDTSC
mentionnée dans plusieurs des autres réponses ici. J'ai donc pensé montrer comment utiliser RDTSC
avec <chrono>
. De plus, je montrerai comment vous pouvez modéliser le code de test sur l'horloge de manière à pouvoir basculer rapidement entre RDTSC
et les fonctionnalités d'horloge intégrées de votre système (qui seront probablement basées sur clock()
, clock_gettime()
et/ou QueryPerformanceCounter
.
Notez que l'instruction RDTSC
est spécifique à x86. QueryPerformanceCounter
est uniquement sous Windows. Et clock_gettime()
est uniquement POSIX. Ci-dessous, je présente deux nouvelles horloges: std::chrono::high_resolution_clock
Et std::chrono::system_clock
, Qui, si vous pouvez supposer que C++ 11, sont maintenant multiplates-formes.
Premièrement, voici comment créer une horloge compatible C++ 11 à partir de l'instruction d'assemblage Intel rdtsc
. Je l'appellerai 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
Toute cette horloge compte les cycles de la CPU et les stocke dans un entier non signé de 64 bits. Vous devrez peut-être modifier la syntaxe du langage d'assemblage pour votre compilateur. Ou bien votre compilateur peut proposer un élément intrinsèque que vous pouvez utiliser à la place (par exemple, now() {return __rdtsc();}
).
Pour construire une horloge, vous devez lui donner la représentation (type de stockage). Vous devez également fournir la période d'horloge, qui doit être une constante de temps de compilation, même si votre machine peut changer de vitesse d'horloge dans différents modes d'alimentation. Et à partir de ceux-ci, vous pouvez facilement définir la durée "native" de votre horloge et son heure en fonction de ces principes fondamentaux.
Si tout ce que vous voulez faire est de sortir le nombre de ticks d'horloge, le nombre que vous donnez pour la période d'horloge n'a pas vraiment d'importance. Cette constante n'entre en jeu que si vous souhaitez convertir le nombre d'impulsions d'horloge en une unité en temps réel telle que la nanoseconde. Et dans ce cas, plus vous êtes en mesure de fournir la vitesse d'horloge, plus la conversion en nanosecondes (millisecondes, peu importe) sera précise.
L'exemple de code ci-dessous montre comment utiliser x::clock
. En fait, j'ai modélisé le code sur l'horloge et j'aimerais montrer comment utiliser plusieurs horloges différentes avec la même syntaxe. Ce test particulier montre ce que la surcharge de la boucle est lorsque vous exécutez ce que vous voulez chronométrer sous une boucle:
#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";
}
La première chose que ce code fait est de créer une unité "en temps réel" pour afficher les résultats. J'ai choisi les picosecondes, mais vous pouvez choisir les unités de votre choix, qu'elles soient intégrales ou à virgule flottante. A titre d'exemple, il y a une unité prédéfinie std::chrono::nanoseconds
Que j'aurais pu utiliser.
Comme autre exemple, je veux imprimer le nombre moyen de cycles d’horloge par itération sous forme de virgule flottante. Je crée donc une autre durée, basée sur le double, qui a les mêmes unités que le tick de l’horloge (appelé Cycle
dans le code).
La boucle est chronométrée avec des appels à clock::now()
de chaque côté. Si vous souhaitez nommer le type renvoyé par cette fonction, il est:
typename clock::time_point t0 = clock::now();
(comme le montre clairement l'exemple x::clock
, et cela vaut également pour les horloges fournies par le système).
Pour obtenir une durée en termes d'horloge à virgule flottante, il suffit de soustraire les deux points de temps, et pour obtenir la valeur par itération, divisez cette durée par le nombre d'itérations.
Vous pouvez obtenir le nombre dans n'importe quelle durée en utilisant la fonction membre count()
. Ceci retourne la représentation interne. Enfin, j’utilise std::chrono::duration_cast
Pour convertir la durée Cycle
en durée picoseconds
et l’afficher.
Pour utiliser ce code est simple:
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>();
}
Ci-dessus, j'exerce le test à l'aide de notre x::clock
Fait maison et je compare ces résultats à l'aide de deux horloges fournies par le système: std::chrono::high_resolution_clock
Et std::chrono::system_clock
. Pour moi cela imprime:
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
Cela montre que chacune de ces horloges a une période de tick différente, car les ticks par itération sont très différents pour chaque horloge. Toutefois, une fois convertie en unité de temps connue (p. Ex. Picosecondes), le résultat obtenu est à peu près identique pour chaque horloge (votre kilométrage peut varier).
Notez que mon code est totalement exempt de "constantes de conversion magiques". En effet, il n'y a que deux nombres magiques dans tout l'exemple:
x::clock
.Avec ce niveau de précision, il serait préférable de raisonner dans le tick du processeur plutôt que dans l'appel système comme clock () . Et n'oubliez pas que s'il faut plus d'une nanoseconde pour exécuter une instruction ... il est pratiquement impossible d'obtenir une précision à la nanoseconde.
Pourtant, quelque chose comme ça est un début:
Voici le code qui permet de récupérer le nombre de ticks d'horloge de la CPU 80x86 passés depuis le dernier démarrage de la CPU. Cela fonctionnera sur Pentium et au-dessus (386/486 non pris en charge). Ce code est en fait spécifique à MS Visual C++, mais peut être probablement très facilement transféré vers tout le reste, tant qu’il supporte l’assemblage en ligne.
inline __int64 GetCpuClocks()
{
// Counter
struct { int32 low, high; } counter;
// Use RDTSC instruction to get clocks count
__asm Push EAX
__asm Push EDX
__asm __emit 0fh __asm __emit 031h // RDTSC
__asm mov counter.low, EAX
__asm mov counter.high, EDX
__asm pop EDX
__asm pop EAX
// Return result
return *(__int64 *)(&counter);
}
Cette fonction présente également l’avantage d’être extrêmement rapide: son exécution ne nécessite généralement pas plus de 50 cycles de traitement.
tilisation des valeurs de chronométrage :
Si vous devez convertir les compteurs en temps réel, divisez les résultats par la vitesse d'horloge de votre puce. N'oubliez pas que le GHz "nominal" sera probablement légèrement différent de la vitesse réelle de votre puce. Pour vérifier la vitesse réelle de votre puce, vous pouvez utiliser plusieurs très bons utilitaires ou l'appel Win32, QueryPerformanceFrequency ().
Pour faire cela correctement, vous pouvez utiliser l'une des deux méthodes, soit avec RDTSC
, soit avec clock_gettime()
. La seconde est environ 2 fois plus rapide et offre l’avantage de donner le temps absolu qui convient. Notez que pour que RDTSC
fonctionne correctement, vous devez l’utiliser comme indiqué (d’autres commentaires sur cette page comportent des erreurs et peuvent produire des valeurs de synchronisation incorrectes sur certains processeurs).
inline uint64_t rdtsc()
{
uint32_t lo, hi;
__asm__ __volatile__ (
"xorl %%eax, %%eax\n"
"cpuid\n"
"rdtsc\n"
: "=a" (lo), "=d" (hi)
:
: "%ebx", "%ecx" );
return (uint64_t)hi << 32 | lo;
}
et pour clock_gettime: (j'ai choisi la résolution en microsecondes de façon arbitraire)
#include <time.h>
#include <sys/timeb.h>
// needs -lrt (real-time lib)
// 1970-01-01 Epoch UTC time, 1 mcs resolution (divide by 1M to get time_t)
uint64_t ClockGetTime()
{
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec / 1000LL;
}
le timing et les valeurs produites:
Absolute values:
rdtsc = 4571567254267600
clock_gettime = 1278605535506855
Processing time: (10000000 runs)
rdtsc = 2292547353
clock_gettime = 1031119636
J'utilise les éléments suivants pour obtenir les résultats souhaités:
#include <time.h>
#include <iostream>
using namespace std;
int main (int argc, char** argv)
{
// reset the clock
timespec tS;
tS.tv_sec = 0;
tS.tv_nsec = 0;
clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tS);
...
... <code to check for the time to be put here>
...
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS);
cout << "Time taken is: " << tS.tv_sec << " " << tS.tv_nsec << endl;
return 0;
}
Pour C++ 11 , voici un wrapper simple:
#include <iostream>
#include <chrono>
class Timer
{
public:
Timer() : beg_(clock_::now()) {}
void reset() { beg_ = clock_::now(); }
double elapsed() const {
return std::chrono::duration_cast<second_>
(clock_::now() - beg_).count(); }
private:
typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> beg_;
};
Ou pour C++ 03 sur * nix,
class Timer
{
public:
Timer() { clock_gettime(CLOCK_REALTIME, &beg_); }
double elapsed() {
clock_gettime(CLOCK_REALTIME, &end_);
return end_.tv_sec - beg_.tv_sec +
(end_.tv_nsec - beg_.tv_nsec) / 1000000000.;
}
void reset() { clock_gettime(CLOCK_REALTIME, &beg_); }
private:
timespec beg_, end_;
};
Exemple d'utilisation:
int main()
{
Timer tmr;
double t = tmr.elapsed();
std::cout << t << std::endl;
tmr.reset();
t = tmr.elapsed();
std::cout << t << std::endl;
return 0;
}
En général, pour déterminer le temps nécessaire pour appeler une fonction, vous devez le faire plusieurs fois de plus qu’une seule fois. Si vous appelez votre fonction une seule fois et que l'exécution prend très peu de temps, vous avez toujours le temps d'appeler les fonctions de minuterie et vous ne savez pas combien de temps cela prend.
Par exemple, si vous estimez que votre fonction peut prendre 800 ns à exécuter, appelez-la dans une boucle dix millions de fois (ce qui prendra alors environ 8 secondes). Divisez le temps total par dix millions pour obtenir le temps par appel.
Vous pouvez utiliser la fonction suivante avec gcc exécuté sous des processeurs x86:
unsigned long long rdtsc()
{
#define rdtsc(low, high) \
__asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
unsigned int low, high;
rdtsc(low, high);
return ((ulonglong)high << 32) | low;
}
avec Digital Mars C++:
unsigned long long rdtsc()
{
_asm
{
rdtsc
}
}
qui lit le minuteur haute performance sur la puce. Je l'utilise lorsque je fais du profilage.
Vous pouvez utiliser Embedded Profiler (gratuit pour Windows et Linux) qui possède une interface avec un minuteur multiplateforme (dans un nombre de cycles de processeur) et peut vous donner un nombre de cycles par seconde:
EProfilerTimer timer;
timer.Start();
... // Your code here
const uint64_t number_of_elapsed_cycles = timer.Stop();
const uint64_t nano_seconds_elapsed =
mumber_of_elapsed_cycles / (double) timer.GetCyclesPerSecond() * 1000000000;
Le recalcul du nombre de cycles dans le temps est peut-être une opération dangereuse avec les processeurs modernes où la fréquence du processeur peut être changée de manière dynamique. Par conséquent, pour être sûr que les temps convertis sont corrects, il est nécessaire de fixer la fréquence du processeur avant le profilage.
J'utilise le code Borland. Voici le code que ti_hund me donne parfois un nombre négatif, mais le temps est assez bon.
#include <dos.h>
void main()
{
struct time t;
int Hour,Min,Sec,Hun;
gettime(&t);
Hour=t.ti_hour;
Min=t.ti_min;
Sec=t.ti_sec;
Hun=t.ti_hund;
printf("Start time is: %2d:%02d:%02d.%02d\n",
t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);
....
your code to time
...
// read the time here remove Hours and min if the time is in sec
gettime(&t);
printf("\nTid Hour:%d Min:%d Sec:%d Hundreds:%d\n",t.ti_hour-Hour,
t.ti_min-Min,t.ti_sec-Sec,t.ti_hund-Hun);
printf("\n\nAlt Ferdig Press a Key\n\n");
getch();
} // end main
Si vous avez besoin d'une précision inférieure à la seconde, vous devez utiliser des extensions spécifiques au système. Vous devrez vérifier avec la documentation du système d'exploitation. POSIX prend en charge jusqu’à quelques microsecondes avec gettimeofday , mais rien de plus précis, les ordinateurs n’ayant pas de fréquences supérieures à 1 GHz.
Si vous utilisez Boost, vous pouvez vérifier boost :: posix_time .
En utilisant la méthode de Brock Adams, avec une classe simple:
int get_cpu_ticks()
{
LARGE_INTEGER ticks;
QueryPerformanceFrequency(&ticks);
return ticks.LowPart;
}
__int64 get_cpu_clocks()
{
struct { int32 low, high; } counter;
__asm cpuid
__asm Push EDX
__asm rdtsc
__asm mov counter.low, EAX
__asm mov counter.high, EDX
__asm pop EDX
__asm pop EAX
return *(__int64 *)(&counter);
}
class cbench
{
public:
cbench(const char *desc_in)
: desc(strdup(desc_in)), start(get_cpu_clocks()) { }
~cbench()
{
printf("%s took: %.4f ms\n", desc, (float)(get_cpu_clocks()-start)/get_cpu_ticks());
if(desc) free(desc);
}
private:
char *desc;
__int64 start;
};
Exemple d'utilisation:
int main()
{
{
cbench c("test");
... code ...
}
return 0;
}
Résultat:
le test a pris: 0.0002 ms
A un peu d'appel de fonction, mais devrait être encore assez rapide :)
Si l'idée est d'avoir une structure minimaliste que vous pouvez utiliser pour des tests rapides, alors je vous suggère de simplement copier et coller n'importe où dans votre fichier C++ juste après les #include
. C'est le seul cas dans lequel je sacrifie une mise en forme de style Allman.
Vous pouvez facilement ajuster la précision dans la première ligne de la structure. Les valeurs possibles sont: nanoseconds
, microseconds
, milliseconds
, seconds
, minutes
ou hours
.
#include <chrono>
struct MeasureTime
{
using precision = std::chrono::microseconds;
std::vector<std::chrono::steady_clock::time_point> times;
std::chrono::steady_clock::time_point oneLast;
void p() {
std::cout << "Mark "
<< times.size()/2
<< ": "
<< std::chrono::duration_cast<precision>(times.back() - oneLast).count()
<< std::endl;
}
void m() {
oneLast = times.back();
times.Push_back(std::chrono::steady_clock::now());
}
void t() {
m();
p();
m();
}
MeasureTime() {
times.Push_back(std::chrono::steady_clock::now());
}
};
MeasureTime m; // first time is already in memory
doFnc1();
m.t(); // Mark 1: next time, and print difference with previous mark
doFnc2();
m.t(); // Mark 2: next time, and print difference with previous mark
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.t(); // prints 'Mark 3: 123123' etc...
Mark 1: 123
Mark 2: 32
Mark 3: 433234
Si vous voulez le rapport après, parce que, par exemple, votre code situé entre les deux écrit également sur la sortie standard. Ajoutez ensuite la fonction suivante à la structure (juste avant MeasureTime ()):
void s() { // summary
int i = 0;
std::chrono::steady_clock::time_point tprev;
for(auto tcur : times)
{
if(i > 0)
{
std::cout << "Mark " << i << ": "
<< std::chrono::duration_cast<precision>(tprev - tcur).count()
<< std::endl;
}
tprev = tcur;
++i;
}
}
Alors vous pouvez simplement utiliser:
MeasureTime m;
doFnc1();
m.m();
doFnc2();
m.m();
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.m();
m.s();
Ce qui listera toutes les marques exactement comme avant, mais ensuite après que l'autre code soit exécuté. Notez que vous ne devriez pas utiliser à la fois m.s()
et m.t()
.
Si c'est pour Linux, j'utilise la fonction "gettimeofday", qui retourne une structure qui donne les secondes et les microsecondes depuis l'époque. Vous pouvez ensuite utiliser timersub pour soustraire les deux pour obtenir la différence dans le temps et la convertir en précision de votre choix. Cependant, vous spécifiez des nanosecondes et la fonction clock_gettime () est ce que vous recherchez. Il met le temps en secondes et en nanosecondes dans la structure que vous y passez.
Voici une belle Boost minuterie qui fonctionne bien:
//Stopwatch.hpp
#ifndef STOPWATCH_HPP
#define STOPWATCH_HPP
//Boost
#include <boost/chrono.hpp>
//Std
#include <cstdint>
class Stopwatch
{
public:
Stopwatch();
virtual ~Stopwatch();
void Restart();
std::uint64_t Get_elapsed_ns();
std::uint64_t Get_elapsed_us();
std::uint64_t Get_elapsed_ms();
std::uint64_t Get_elapsed_s();
private:
boost::chrono::high_resolution_clock::time_point _start_time;
};
#endif // STOPWATCH_HPP
//Stopwatch.cpp
#include "Stopwatch.hpp"
Stopwatch::Stopwatch():
_start_time(boost::chrono::high_resolution_clock::now()) {}
Stopwatch::~Stopwatch() {}
void Stopwatch::Restart()
{
_start_time = boost::chrono::high_resolution_clock::now();
}
std::uint64_t Stopwatch::Get_elapsed_ns()
{
boost::chrono::nanoseconds nano_s = boost::chrono::duration_cast<boost::chrono::nanoseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(nano_s.count());
}
std::uint64_t Stopwatch::Get_elapsed_us()
{
boost::chrono::microseconds micro_s = boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(micro_s.count());
}
std::uint64_t Stopwatch::Get_elapsed_ms()
{
boost::chrono::milliseconds milli_s = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(milli_s.count());
}
std::uint64_t Stopwatch::Get_elapsed_s()
{
boost::chrono::seconds sec = boost::chrono::duration_cast<boost::chrono::seconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(sec.count());
}
Qu'est ce que tu penses de ça:
int iceu_system_GetTimeNow(long long int *res)
{
static struct timespec buffer;
//
#ifdef __CYGWIN__
if (clock_gettime(CLOCK_REALTIME, &buffer))
return 1;
#else
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &buffer))
return 1;
#endif
*res=(long long int)buffer.tv_sec * 1000000000LL + (long long int)buffer.tv_nsec;
return 0;
}