111 votes

Fonction de minuterie C ++ pour fournir le temps en nanosecondes

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, je souhaite obtenir le même en nanosecondes et avec plus de précision.

86voto

grieve Points 6303

Ce que les autres ont posté sur l'exécution de la fonction plusieurs fois dans une boucle est correcte.

Pour Linux et BSD) que 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 windows, vous souhaitez utiliser le QueryPerformanceCounter. Et ici, c'est plus sur la QPC

Apparemment il y est un problème avec la QPC sur certains chipsets, de sorte que vous pouvez assurez-vous que vous n'avez pas ces chipset. De plus, certains dual core d'Amd peut aussi causer un problème. Voir le second post par sebbbi, où il déclare:

QueryPerformanceCounter() et QueryPerformanceFrequency() offre un peu meilleure résolution, mais ont différentes questions. Par exemple, dans Windows XP, tous les AMD Athlon X2 dual core retourner le PC de l'un de les cœurs "au hasard" (le PC parfois saute un peu à l'envers), à moins que vous spécialement installer AMD dual core pilote pour réparer le problème. Nous n'avons pas remarqué tout autre double+ core ayant des problèmes similaires (p4 dual, p4 ht, core2 double, core 2 quad, phenom quad).

EDIT 2013/07/16:

Il ressemble à il ya une certaine controverse sur l'efficacité de la 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 généralement ajuster pour plusieurs processeurs, des bugs dans le BIOS ou de pilotes pourrait entraîner dans ces routines de retour les différentes valeurs que le fil se déplace d'un processeur à l'autre...

Cependant, cette StackOverflow répondre http://stackoverflow.com/a/4588605/34329 les états qui QPC) devrait fonctionner sur n'importe quel système d'exploitation MS après Win XP service pack 2.

Cet article montre que Windows 7 peut déterminer si le processeur(s) ont un invariant TSC et revient à une minuterie externe si 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.

Une autre amende de lecture liées à timers:

Voir les commentaires pour plus de détails.

73voto

Howard Hinnant Points 59526

Mise à jour de réponse pour une question aussi ancienne.

Cette nouvelle réponse utilise C++11 <chrono> des installations. Bien qu'il existe d'autres réponses qui montrent comment utiliser <chrono>, aucun d'entre eux montre comment utiliser <chrono> avec l' RDTSC installation mentionné dans plusieurs des autres réponses ici. J'ai donc 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 rapidement basculer entre RDTSC et de votre système d'horloge intégrée des installations (ce qui sera probablement fondée sur clock(), clock_gettime() et/ou QueryPerformanceCounter.

Notez que RDTSC est Intel uniquement. QueryPerformanceCounter s'applique uniquement à Windows. Et clock_gettime() est POSIX. Ci-dessous j'ai introduit deux nouvelles horloges: std::chrono::high_resolution_clock et std::chrono::system_clock, qui, si l'on peut supposer C++11, sont maintenant de la croix-plate-forme.

Tout d'abord, voici une façon de créer un C++11-compatible horloge de l'Intel rdtsc instructions de montage. Je vais l'appeler x::clock:

#include <chrono>

namespace x
{

struct clock
{
    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2800000000>          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 n'est de compter les cycles CPU et de le stocker dans un 64 bits non signé entier. Vous devrez peut-être ajuster l'assemblée de la syntaxe du langage de votre compilateur. Ou votre compilateur peut offrir une intrinsèques 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 un moment de la compilation constante, même si votre machine peut changer la vitesse d'horloge dans les différents modes d'alimentation. Et à partir de ceux que vous pouvez facilement définir votre horloge "native" le temps de la durée et de temps en termes de ces principes fondamentaux.

Si tout ce que vous voulez faire est de sortie, le nombre de tops d'horloge, il n'importe pas vraiment ce que le numéro que vous donnez à la période d'horloge. Cette constante n'est mise en jeu si vous voulez convertir le nombre de tops d'horloge en temps réel de l'unité tels que nanosecondes. Et dans ce cas, la plus précise, vous êtes en mesure de fournir la vitesse d'horloge, plus précise sera la conversion de nanosecondes, (millisecondes, peu importe).

Ci-dessous est un exemple de code qui montre comment utiliser x::clock. En fait, j'ai modélisé le code sur l'horloge que j'aimerais vous montrer comment vous pouvez utiliser différentes horloges avec exactement la même syntaxe. Ce test particulier est en montrant ce que la boucle de la charge lors de l'exécution de ce que vous voulez de temps en vertu d'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 n'est de créer un "temps réel" de l'appareil pour afficher les résultats. J'ai choisi de picosecondes, mais vous pouvez choisir les unités que vous souhaitez, soit entier ou à virgule flottante fonction. Comme un exemple, il y a un pré-fabriqué std::chrono::nanoseconds unité, j'ai pu utiliser.

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

La boucle est chronométré avec des appels à des clock::now() sur chaque côté. Si vous souhaitez nommer le type retourné par cette fonction, il est:

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

(comme le montre clairement le x::clock exemple, et est également vrai de l'fournis par le système d'horloges).

Pour obtenir une durée, en termes de virgule flottante tops d'horloge on se soustrait les deux points dans le temps, et pour obtenir le par itération de la valeur, de diviser cette durée par le nombre d'itérations.

Vous pouvez obtenir le nombre dans la durée à l'aide de la count() de la fonction membre. Ceci renvoie à la représentation interne. Enfin je utiliser std::chrono::duration_cast pour convertir la durée Cycle de 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, je exercer le test à l'aide de notre maison x::clock, et de comparer ces résultats avec l'aide de deux fournis par le système d'horloges: 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 autre tique de cette période, comme les tiques par itération est très différente pour chaque horloge. Cependant lors de la conversion à une unité de temps (par exemple, picosecondes), j'ai environ le même résultat pour chaque horloge (votre kilométrage peut varier).

Notez comment mon code est complètement libre de la "magie constantes de conversion". En effet, il y a seulement deux numéros de magie dans l'ensemble de l'exemple:

  1. La vitesse d'horloge de ma machine afin de définir x::clock.
  2. Le nombre d'itérations de tester plus. Si la modification de ce nombre rend vos résultats varient grandement, alors vous devriez probablement faire le nombre d'itérations plus élevé, ou vider votre ordinateur de processus concurrents lors des tests.

31voto

VonC Points 414372

Avec ce niveau de précision, il serait préférable de raison de cycles de PROCESSEUR plutôt que dans le système de l'appel comme d'horloge(). Et n'oubliez pas que si cela prend plus d'une nanoseconde pour exécuter une instruction... ayant une précision de l'ordre de la nanoseconde est à peu près impossible.

Pourtant, quelque chose comme ça n'est qu'un début:

Voici le code pour récupérer le numéro de 80x86 de l'horloge du PROCESSEUR tiques écoulés depuis la CPU a été démarré. Il fonctionne sur les Pentium et au-dessus (386/486 pas pris en charge). Ce code est en fait MS Visual C++ spécifique, mais peut être probablement très facile porté à tout autre chose, tant qu'il prend en charge assembly 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, il prend généralement pas plus de 50 cycles de processeur à exécuter.

À l'aide du Calendrier les Chiffres:
Si vous avez besoin de traduire le chrono en vrai temps écoulé, divisez le résultat par votre puce de la vitesse de l'horloge. Rappelez-vous que la "cote" GHz est susceptible d'être légèrement différente de la vitesse réelle de votre puce. Pour vérifier votre puce est vrai vitesse, vous pouvez utiliser plusieurs très bons utilitaires ou Win32 appel, QueryPerformanceFrequency().

25voto

Marius Points 2008

Pour ce faire correctement, vous pouvez utiliser l'une des deux façons, soit aller avec RDTSC ou avec clock_gettime(). La deuxième est environ 2 fois plus rapide et a l'avantage de donner le droit absolu du temps. Notez que pour l' RDTSC fonctionne correctement, vous devez l'utiliser comme indiqué dans d'autres commentaires sur cette page a des erreurs, et le rendement peut être incorrecte valeurs de temps 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 une résolution 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 calendrier et les valeurs produites:

Absolute values:
rdtsc           = 4571567254267600
clock_gettime   = 1278605535506855

Processing time: (10000000 runs)
rdtsc           = 2292547353
clock_gettime   = 1031119636

24voto

gagneet Points 4565

J’utilise ce qui suit pour obtenir les résultats souhaités :

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X