Fonction de minuterie pour fournir le temps en nano secondes en utilisant C++

je souhaite calculer le temps qu'il a fallu à une API pour retourner une valeur. Le temps pris pour une telle action est dans l'espace de nano secondes. Comme L'API est une classe/fonction C++, j'utilise la minuterie.h à caculate la même:

  #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 puis-je obtenir la même chose en nano secondes et avec plus de précision?

96
demandé sur Peter Mortensen 0000-00-00 00:00:00

2 réponses

ce que d'autres ont posté à propos de 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 windows vous voulez utiliser le QueryPerformanceCounter . Et voici plus sur QPC

apparemment Il ya un connu question avec QPC sur certains chipsets, donc vous voulez vous assurer que vous n'avez pas ces chipset. En outre, certains AMDs à double noyau peuvent également causer un problème . Voir le second billet de sebbbi, où il déclare:

QueryPerformanceCounter () et QueryPerformanceFrequency () offre une peu meilleure résolution, mais ont différents problèmes. Par exemple, dans Windows XP, all AMD Athlon X2 dual les CPU de base renvoient le PC de l'un ou l'autre des les noyaux " au hasard "(le PC parfois sauts peu à l'envers), à moins que vous installer spécialement le pilote à double noyau AMD pour réparer le problème. Nous n'avons pas n'importe quel autre processeur double ou central questions similaires (p4 dual, p4 ht, core2 double, core 2 quad, phenom quad).

MODIFIER 2013/07/16:

il semble qu'il y ait une certaine 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

...Tandis que QueryPerformanceCounter et QueryPerformanceFrequency ajustent typiquement pour les processeurs multiples, les bugs dans le BIOS ou les pilotes peuvent entraîner le retour de ces routines différentes valeurs lorsque le fil passe d'un processeur à un autre...

Cependant, cela StackOverflow réponse https://stackoverflow.com/a/4588605/34329 indique que QPC devrait fonctionner correctement sur tout système D'exploitation MS après Win XP service pack 2.

cet article montre que Windows 7 peut déterminer si le(s) processeur (s) ont un TSC invariant et retombe sur une minuterie 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 est toujours un problème.

Autre lecture fine liée aux minuteries:

Voir les commentaires pour plus de détails.

79
répondu grieve 2017-05-23 12:32:26

cette nouvelle réponse utilise L'installation <chrono> de C++11. Bien qu'il existe d'autres réponses qui montrent comment utiliser <chrono> , aucun d'entre eux montre l'utilisation de la 151950920" avec la RDTSC établissement mentionné à plusieurs des autres réponses ici. Donc j'ai pensé que je voudrais montrer comment utiliser RDTSC avec <chrono> . En outre, je vais vous montrer comment vous pouvez templatize le code de test sur l'horloge de sorte que vous pouvez passer rapidement entre RDTSC et votre système intégré installations d'horlogerie (qui seront probablement basées sur clock() , clock_gettime() et/ou QueryPerformanceCounter .

notez que l'instruction RDTSC est spécifique à x86. QueryPerformanceCounter est Windows uniquement. Et clock_gettime() est POSIX seulement. Ci-dessous , je présente deux nouvelles horloges: std::chrono::high_resolution_clock et 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 de L'Intel rdtsc instructions de montage. 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 compter les cycles CPU et le stocker dans un entier 64 bits non signé. Vous devrez peut-être modifier la syntaxe du langage d'assemblage de 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 de vous donner la représentation (type de stockage). Vous devez également fournir la période d'horloge, qui doit être une compilez la constante de temps, même si votre machine peut changer la vitesse d'horloge dans différents modes de puissance. Et de ceux que vous pouvez facilement définir la durée "native" de votre horloge et le point de temps en termes de ces fondamentaux.

si tout ce que vous voulez faire est de sortir le nombre de tiques d'horloge, il n'a pas vraiment d'importance quel nombre vous donnez pour la période d'horloge. Cette constante n'entre en jeu que si vous voulez convertir le nombre de tics d'horloge en une unité temps réel telle que nanoseconde. 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).

ci-Dessous est un exemple de code qui montre comment utiliser x::clock . En fait, j'ai modelé le code sur l'horloge comme je voudrais montrer comment vous pouvez utiliser beaucoup d'horloges différentes avec la même syntaxe exacte. Ce test particulier est de montrer ce que la boucle au-dessus est lorsque vous exécutez ce que vous voulez chronométrer 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é" temps réel " pour afficher les résultats. J'ai choisi les picosecondes, mais vous pouvez choisir toutes les unités que vous voulez, qu'elles soient intégrales ou à virgule flottante. Par exemple, il y a une unité std::chrono::nanoseconds que j'aurais pu utiliser.

comme autre exemple je veux imprimer le nombre moyen de cycles d'horloge par itération comme un point flottant, donc je crée une autre durée, basée sur double, qui a les mêmes unités que l'horloge tick (appelé Cycle dans le code).

la boucle est chronométrée avec des appels à clock::now() de chaque côté. Si vous voulez nommer le type retourné par cette fonction, c'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 de point flottant Tic-Tac un simplement soustrait 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 de membre count() . Ceci renvoie la représentation interne. Enfin, j'utilise std::chrono::duration_cast pour convertir la durée Cycle à la durée picoseconds et l'imprimer.

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'effectue le test en utilisant notre x::clock maison , et je compare ces résultats avec l'utilisation de deux des horloges fournies par le système: std::chrono::high_resolution_clock et std::chrono::system_clock . Pour moi, cela m'affiche:

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 Tic-Tac différente, car les TIC-TAC par itération est très différente pour chaque horloge. Cependant, une fois converti à une unité de temps connue (par ex. picosecondes), j'obtiens approximativement le même résultat pour chaque horloge (votre kilométrage peut varier.)

notez comment mon code est complètement libre 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 définir x::clock .
  2. le nombre d'itérations à tester. Si changer ce nombre rend vos résultats varient considérablement, alors vous devriez probablement faire le nombre d'itérations plus élevé, ou vider votre ordinateur de processus concurrents lors des tests.
66
répondu Howard Hinnant 2017-03-29 12:40:38