Fonction de minuterie pour fournir du temps en nanosecondes en utilisant C ++

101

Je souhaite calculer le temps qu'il a fallu à une API pour renvoyer une valeur. Le temps nécessaire pour une telle action est de l'ordre de 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 nanosecondes et avec plus de précision?

gagneet
la source
le code ci-dessus se calcule en quelques secondes, je veux obtenir la réponse en
nanosecondes
Besoin d'ajouter la plateforme à la question (et de préférence au titre également) pour obtenir une bonne réponse.
Patrick Johnmeyer
En plus d'obtenir l'heure, il faut rechercher les problèmes de microbenchmarking (qui est extrêmement complexe) - il est peu probable qu'une seule exécution, et obtenir l'heure au début et à la fin, donne suffisamment de précision.
Blaisorblade
@Blaisorblade: Surtout depuis que j'ai découvert dans certains de mes tests que ce clock()n'est pas aussi rapide que je le pensais.
Mooing Duck

Réponses:

83

Ce que d'autres ont publié sur l'exécution répétée de la fonction dans une boucle est correct.

Pour Linux (et BSD), vous souhaitez 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, vous souhaitez utiliser QueryPerformanceCounter . Et voici plus sur QPC

Apparemment, il y a un problème connu avec QPC sur certains chipsets, donc vous voudrez peut-être vous assurer que vous ne disposez pas de ces chipsets. En outre, certains AMD double cœur peuvent également poser un problème . Voir le deuxième message 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, dans Windows XP, tous les processeurs double cœur AMD Athlon X2 renvoient le PC de l'un ou l'autre des cœurs "au hasard" (le PC saute parfois un peu en arrière), à ​​moins que vous n'installiez spécialement le package de pilotes double cœur AMD pour résoudre le problème. Nous n'avons remarqué aucun autre processeur dual + core ayant des problèmes similaires (p4 dual, p4 ht, core2 dual, core2 quad, phenom quad).

EDIT 2013/07/16:

Il semble qu'il y ait 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 amener ces routines à renvoyer des valeurs différentes lorsque le thread passe d'un processeur à un autre ...

Cependant, cette réponse de StackOverflow https://stackoverflow.com/a/4588605/34329 indique que QPC devrait fonctionner correctement sur n'importe quel système d'exploitation MS après le service pack 2 de Win XP.

Cet article montre que Windows 7 peut déterminer si le (s) processeur (s) ont un TSC invariant et revient à une minuterie externe si ce n'est pas le cas. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html La synchronisation entre les processeurs est toujours un problème.

Autres bonnes lectures liées aux minuteries:

Voir les commentaires pour plus de détails.

pleurer
la source
1
J'ai vu l'horloge TSC biaiser sur un ancien PC double Xeon, mais pas aussi mauvais que sur un Athlon X2 avec la rampe d'horloge C1 activée. Avec la rampe d'horloge C1, l'exécution d'une instruction HLT ralentit l'horloge, ce qui fait que le TSC sur les cœurs inactifs s'incrémentera plus lentement que sur les cœurs actifs.
bk1e
6
CLOCK_MONOTONIC fonctionne sur les versions de Linux dont je dispose.
Bernard
1
@Bernard - Cela doit être récemment ajouté depuis que j'ai regardé cela pour la dernière fois. Merci pour l'information.
pleurer le
3
En fait, vous devez utiliser CLOCK_MONOTONIC_RAW, s'il est disponible, afin d'obtenir l'heure du matériel non ajustée par NTP.
Comme discuté ici, l'implémentation correcte de QPC n'utilise pas le compteur TSC, du moins là où il est connu pour être peu fiable: stackoverflow.com/q/510462/53974
Blaisorblade
69

Cette nouvelle réponse utilise la fonctionnalité de C ++ 11 <chrono>. Bien qu'il existe d'autres réponses qui montrent comment utiliser <chrono>, aucune d'elles ne montre comment utiliser <chrono>avec l' RDTSCinstallation mentionnée dans plusieurs des autres réponses ici. J'ai donc pensé montrer comment utiliser RDTSCavec <chrono>. De plus, je vais vous montrer comment vous pouvez modéliser le code de test sur l'horloge afin de pouvoir basculer rapidement entre RDTSCles fonctions d'horloge intégrées de votre système (qui seront probablement basées sur clock(), clock_gettime()et / ou QueryPerformanceCounter.

Notez que l' RDTSCinstruction est spécifique à x86. QueryPerformanceCounterest Windows uniquement. Et clock_gettime()c'est POSIX uniquement. Ci-dessous, j'introduis deux nouvelles horloges: std::chrono::high_resolution_clocket std::chrono::system_clock, qui, si vous pouvez supposer C ++ 11, sont maintenant multiplateformes.

Tout d'abord, voici comment créer une horloge compatible C ++ 11 à partir des rdtscinstructions d'assemblage Intel . 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

Tout ce que cette horloge fait est de compter les cycles CPU et de les stocker dans un entier 64 bits non signé. Vous devrez peut-être modifier la syntaxe du langage d'assemblage pour votre compilateur. Ou votre compilateur peut offrir un 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 la vitesse d'horloge dans différents modes d'alimentation. Et à partir de ceux-ci, vous pouvez facilement définir la durée et le point temporel "natifs" de votre horloge en fonction de ces principes fondamentaux.

Si tout ce que vous voulez faire est de sortir le nombre de coups d'horloge, peu importe le nombre que vous donnez pour la période d'horloge. Cette constante n'entre en jeu que si vous souhaitez convertir le nombre de tics d'horloge en une unité en temps réel telle que les nanosecondes. Et dans ce cas, plus vous êtes en mesure de fournir la vitesse d'horloge avec précision, plus la conversion en nanosecondes sera précise (millisecondes, peu importe).

Voici un exemple de code qui montre comment utiliser x::clock. En fait, j'ai modelé le code sur l'horloge car j'aimerais montrer comment vous pouvez utiliser de nombreuses horloges différentes avec exactement la même syntaxe. Ce test particulier montre quelle est la surcharge de la boucle lors de l'exécution de 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 fait ce code est de créer une unité "en temps réel" pour afficher les résultats. J'ai choisi les picosecondes, mais vous pouvez choisir toutes les unités que vous aimez, en virgule flottante ou intégrale. À titre d'exemple, il y a une std::chrono::nanosecondsunité préfabriquée 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, donc je crée une autre durée, basée sur le double, qui a les mêmes unités que le tick de l'horloge (appelé Cycledans le code).

La boucle est chronométrée avec des appels de clock::now()chaque côté. Si vous souhaitez nommer le type renvoyé par cette fonction, c'est:

typename clock::time_point t0 = clock::now();

(comme clairement montré dans l' x::clockexemple, et cela est également vrai pour les horloges fournies par le système).

Pour obtenir une durée en termes de ticks d'horloge en virgule flottante, on soustrait simplement les deux points temporels, et pour obtenir la valeur par itération, divisez cette durée par le nombre d'itérations.

Vous pouvez obtenir le décompte dans n'importe quelle durée à l'aide de la count()fonction membre. Cela renvoie la représentation interne. Enfin, j'utilise std::chrono::duration_castpour convertir la durée Cycleen durée picosecondset l'imprimer.

Pour utiliser ce code, c'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 en utilisant notre produit fait maison x::clocket je compare ces résultats avec deux des horloges fournies par le système: std::chrono::high_resolution_clocket std::chrono::system_clock. Pour moi, ceci s'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 ticks différente, car les ticks par itération sont très différents pour chaque horloge. Cependant, une fois converti en une unité de temps connue (par exemple, picosecondes), j'obtiens approximativement le même résultat 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:

  1. La vitesse d'horloge de ma machine afin de la définir x::clock.
  2. Le nombre d'itérations à tester. Si la modification de ce nombre fait varier considérablement vos résultats, vous devriez probablement augmenter le nombre d'itérations ou vider votre ordinateur des processus concurrents pendant le test.
Howard Hinnant
la source
5
Par "RDTSC est uniquement Intel", vous faites vraiment référence à l'architecture x86 et à ses dérivés, n'est-ce pas? Les puces AMD, Cyrix, Transmeta x86 ont l'instruction , et les processeurs Intel RISC et ARM ne le font pas.
Ben Voigt
1
@BenVoigt: +1 Oui, votre correction est tout à fait correcte, merci.
Howard Hinnant
1
Comment la limitation du processeur affectera-t-elle cela? La vitesse d'horloge ne change-t-elle pas en fonction de la charge du processeur?
Tejas Kale
@TejasKale: Ceci est décrit dans la réponse dans les deux paragraphes consécutifs commençant par "Pour construire une horloge vous ...". En règle générale, le code de synchronisation ne mesure pas le travail qui bloque un thread (mais il le peut). Et si généralement votre CPU ne ralentit pas. Mais si vous mesurez du code impliquant le sommeil, le verrouillage mutex, l'attente condition_variable, etc., l' rdtschorloge aura probablement des conversions inexactes vers d'autres unités. C'est une bonne idée de configurer vos mesures afin de pouvoir facilement changer et comparer les horloges (comme indiqué dans cette réponse).
Howard Hinnant
28

Avec ce niveau de précision, il serait préférable de raisonner en tick CPU plutôt qu'en appel système comme clock () . Et n'oubliez pas que s'il faut plus d'une nanoseconde pour exécuter une instruction ... avoir une précision de nanoseconde est quasiment impossible.

Encore, quelque chose comme ça est un début:

Voici le code réel pour récupérer le nombre de ticks d'horloge du processeur 80x86 passés depuis le dernier démarrage du processeur. Il fonctionnera sur Pentium et supérieur (386/486 non pris en charge). Ce code est en fait spécifique à MS Visual C ++, mais peut être probablement très facilement porté vers n'importe quoi d'autre, tant qu'il prend en charge 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 a également l'avantage d'être extrêmement rapide - son exécution ne prend généralement pas plus de 50 cycles CPU.

Utilisation des chiffres de chronométrage :
Si vous avez besoin de traduire les comptages d'horloge en temps réel écoulé, divisez les résultats par la vitesse d'horloge de votre puce. N'oubliez pas que le GHz «nominal» est susceptible d'être 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 ().

VonC
la source
merci pour l'information, c'est utile. je n'ai pas pensé aux cycles cpu pour calculer le temps, je pense que c'est un très bon point à garder à l'esprit :-)
gagneet
4
L'utilisation de QueryPerformanceFrequency () pour transformer les comptes TSC en temps écoulé peut ne pas fonctionner. QueryPerformanceCounter () utilise le HPET (High Precision Event Timer) sur Vista lorsqu'il est disponible. Il utilise le minuteur de gestion de l'alimentation ACPI si l'utilisateur ajoute / USEPMTIMER à boot.ini.
bk1e
23

Pour ce faire correctement, vous pouvez utiliser l'une des deux méthodes suivantes, soit avec, RDTSCsoit avec clock_gettime(). La seconde est environ 2 fois plus rapide et a l'avantage de donner le bon temps absolu. Notez que pour RDTSCfonctionner correctement, vous devez l'utiliser comme indiqué (d'autres commentaires sur cette page comportent des erreurs et peuvent donner 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 arbitrairement la résolution microseconde)

#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
Marius
la source
22

J'utilise ce qui suit 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;
}
gagneet
la source
2
J'ai voté contre parce qu'en essayant d'appliquer ce code, je devais d'abord chercher sur google pourquoi le timespec n'est pas défini. Ensuite, j'ai dû google ce qui est POSIX ... et si je l'ai compris, ce code n'est pas pertinent pour les utilisateurs de Windows qui doivent s'en tenir à la bibliothèque standard.
Daniel Katz
8

Pour C ++ 11 , voici un simple wrapper:

#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;
}

Sur https://gist.github.com/gongzhitaao/7062087

gongzhitaao
la source
5

En général, pour déterminer combien de temps il faut pour appeler une fonction, vous voulez le faire plusieurs fois qu'une seule fois. Si vous appelez votre fonction une seule fois et que son exécution prend très peu de temps, vous avez toujours la surcharge 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 pour s'exécuter, appelez-la en 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.

Greg Hewgill
la source
en fait, j'essaie d'obtenir les performances de l'API pour un appel particulier. pour chaque exécution, cela peut donner un temps différent, cela peut affecter le graphique que je fais pour l'amélioration des performances ... d'où le temps en nanosecondes. mais oui, c'est une excellente idée, je vais y réfléchir.
gagneet
5

Vous pouvez utiliser la fonction suivante avec gcc fonctionnant 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 la minuterie haute performance sur la puce. J'utilise ceci lors du profilage.

Walter Bright
la source
2
cela est utile, je vais vérifier si le processeur est x86, car
j'utilise
1
Quelles valeurs l'utilisateur est-il censé donner pour le haut et le bas? Pourquoi définissez-vous une macro dans le corps d'une fonction? De plus, ulonglong, vraisemblablement typé à unsigned long long, n'est pas un type standard. J'aimerais l'utiliser mais je ne sais pas comment;)
Joseph Garvin
1
unsigned long n'est pas la bonne chose à utiliser sous linux. Vous voudrez peut-être envisager d'utiliser int à la place car long et long long sont tous deux 64 bits sur Linux 64 bits.
Marius
3
Le compteur TSC est de nos jours souvent peu fiable: il change sa vitesse sur de nombreux processeurs lorsque la fréquence est modifiée, et est incohérent entre les différents cœurs, par conséquent le TSC ne croît pas toujours.
Blaisorblade
1
@Marius: J'ai implémenté votre commentaire, en utilisant unsigned intcomme type interne.
Blaisorblade
3

Si vous avez besoin d'une précision inférieure à la seconde, vous devez utiliser des extensions spécifiques au système et vous devrez consulter la documentation du système d'exploitation. POSIX prend en charge jusqu'à microsecondes avec gettimeofday , mais rien de plus précis puisque les ordinateurs n'avaient pas de fréquences supérieures à 1 GHz.

Si vous utilisez Boost, vous pouvez vérifier boost :: posix_time .

Raymond Martineau
la source
voulez garder le code portable, verra la bibliothèque boost et vérifierai si je peux le regrouper avec le code. merci :-)
gagneet
3

J'utilise le code Borland, voici le code ti_hund qui me donne parfois un nombre négatif mais le timing 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
Paul J Moesman
la source
3

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 une surcharge d'appel de fonction, mais devrait être encore plus que suffisamment rapide :)

Thomas
la source
3

Vous pouvez utiliser Embedded Profiler (gratuit pour Windows et Linux) qui a 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 en temps est peut-être une opération dangereuse avec les processeurs modernes où la fréquence du processeur peut être modifié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.

Mi-La
la source
2

Si c'est pour Linux, j'utilise la fonction "gettimeofday", qui retourne une structure qui donne les secondes et les microsecondes depuis l'Epoch. Vous pouvez ensuite utiliser timersub pour soustraire les deux pour obtenir la différence de temps et la convertir en la précision de temps souhaitée. Cependant, vous spécifiez des nanosecondes, et il semble que la fonction clock_gettime () soit ce que vous recherchez. Il met le temps en termes de secondes et de nanosecondes dans la structure que vous y passez.

Will Mc
la source
clock_gettime () devrait faire l'affaire pour le moment. essaiera d'utiliser la même chose pour mon but ...
gagneet
2

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;
    }
glace
la source
2

Voici une belle minuterie Boost 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());
}
Patrick K
la source
2

Copier-coller-struct minimaliste + utilisation paresseuse

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 le#include 's. C'est le seul cas dans lequel je sacrifie le formatage 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, minutesou 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());
    }
};

Usage

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...

Résultat de sortie standard

Mark 1: 123
Mark 2: 32
Mark 3: 433234

Si vous voulez un résumé après l'exécution

Si vous voulez le rapport après, parce que, par exemple, votre code 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 comme avant, mais après l'exécution de l'autre code. Notez que vous ne devez pas utiliser à la fois m.s()et m.t().

Yéti
la source
Fonctionne parfaitement avec OpenMP sur Ubuntu 16.04. Merci beaucoup, cela devrait être la meilleure réponse à l'OMI!
Íhor Mé