web-dev-qa-db-fra.com

Comment calculer le temps d'exécution d'un extrait de code en C++

Je dois calculer le temps d'exécution d'un fragment de code C++ en secondes. Il doit fonctionner sur des machines Windows ou Unix.

J'utilise le code le code suivant pour le faire. (importer avant)

clock_t startTime = clock();
// some code here
// to compute its execution duration in runtime
cout << double( clock() - startTime ) / (double)CLOCKS_PER_SEC<< " seconds." << endl;

Cependant, pour de petites entrées ou de courtes déclarations telles que a = a + 1, le résultat est "0 secondes". Je pense que ce doit être quelque chose comme 0,0000001 secondes ou quelque chose comme ça.

Je me souviens que System.nanoTime() en Java fonctionne plutôt bien dans ce cas. Cependant, je ne peux pas obtenir la même fonctionnalité exacte de la fonction clock() de C++.

Avez-vous une solution?

113
AhmetB - Google

Vous pouvez utiliser cette fonction que j'ai écrite. Vous appelez GetTimeMs64(), qui renvoie le nombre de millisecondes écoulées depuis l’époque unix à l’aide de l’horloge système - le même principe que time(NULL), sauf en millisecondes.

Cela fonctionne à la fois sous windows et linux; c'est thread-safe.

Notez que la granularité est de 15 ms sur les fenêtres; sur linux, cela dépend de la mise en oeuvre, mais cela prend généralement 15 ms.

#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#include <ctime>
#endif

/* Remove if already defined */
typedef long long int64; typedef unsigned long long uint64;

/* Returns the amount of milliseconds elapsed since the UNIX Epoch. Works on both
 * windows and linux. */

uint64 GetTimeMs64()
{
#ifdef _WIN32
 /* Windows */
 FILETIME ft;
 LARGE_INTEGER li;

 /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
  * to a LARGE_INTEGER structure. */
 GetSystemTimeAsFileTime(&ft);
 li.LowPart = ft.dwLowDateTime;
 li.HighPart = ft.dwHighDateTime;

 uint64 ret = li.QuadPart;
 ret -= 116444736000000000LL; /* Convert from file time to UNIX Epoch time. */
 ret /= 10000; /* From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals */

 return ret;
#else
 /* Linux */
 struct timeval tv;

 gettimeofday(&tv, NULL);

 uint64 ret = tv.tv_usec;
 /* Convert from micro seconds (10^-6) to milliseconds (10^-3) */
 ret /= 1000;

 /* Adds the seconds (10^0) after converting them to milliseconds (10^-3) */
 ret += (tv.tv_sec * 1000);

 return ret;
#endif
}
112
Andreas Bonini

J'ai un autre exemple de travail qui utilise des microsecondes (UNIX, POSIX, etc.).

    #include <sys/time.h>
    typedef unsigned long long timestamp_t;

    static timestamp_t
    get_timestamp ()
    {
      struct timeval now;
      gettimeofday (&now, NULL);
      return  now.tv_usec + (timestamp_t)now.tv_sec * 1000000;
    }

    ...
    timestamp_t t0 = get_timestamp();
    // Process
    timestamp_t t1 = get_timestamp();

    double secs = (t1 - t0) / 1000000.0L;

Voici le fichier où nous avons codé ceci:

https://github.com/arhuaco/junkcode/blob/master/emqbit-bench/bench.c

42
arhuaco

Voici une solution simple en C++ 11 qui vous donne une résolution satisfaisante.

#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 sur * nix, pour c ++ 03

#include <iostream>
#include <ctime>

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

Voici l'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;
}

De https://Gist.github.com/gongzhitaao/7062087

32
gongzhitaao
#include <boost/progress.hpp>

using namespace boost;

int main (int argc, const char * argv[])
{
  progress_timer timer;

  // do stuff, preferably in a 100x loop to make it take longer.

  return 0;
}

Lorsque progress_timer sort du cadre, il affiche le temps écoulé depuis sa création.

UPDATE: J'ai effectué un remplacement autonome simple (OSX/iOS mais facile à porter): https://github.com/catnapgames/TestTimerScoped

18
Tomas Andrle

Windows fournit la fonction QueryPerformanceCounter () et Unix a gettimeofday () Les deux fonctions peuvent mesurer une différence d'au moins 1 micro-seconde. 

5
Captain Comic

Dans certains programmes que j'ai écrits, j'ai utilisé RDTS à cette fin. RDTSC n'est pas une question de temps mais de nombre de cycles à compter du démarrage du processeur. Vous devez le calibrer sur votre système pour obtenir un résultat en une seconde, mais c'est très pratique lorsque vous souhaitez évaluer les performances. Il est même préférable d'utiliser le nombre de cycles directement sans essayer de les remettre en secondes.

(le lien ci-dessus est dirigé vers une page wikipedia française, mais il contient des exemples de code C++, la version anglaise est ici )

3
kriss

(solution spécifique à Windows) La méthode actuelle (vers 2017) pour obtenir un minutage précis sous Windows consiste à utiliser "QueryPerformanceCounter". Cette approche présente l'avantage de fournir des résultats très précis et est recommandée par les États membres. Il suffit de placer le blob de code dans une nouvelle application de console pour obtenir un exemple de travail. Il y a une longue discussion ici: Acquisition d'horodatages haute résolution

#include <iostream>
#include <tchar.h>
#include <windows.h>

int main()
{
constexpr int MAX_ITER{ 10000 };
constexpr __int64 us_per_hour{ 3600000000ull }; // 3.6e+09
constexpr __int64 us_per_min{ 60000000ull };
constexpr __int64 us_per_sec{ 1000000ull };
constexpr __int64 us_per_ms{ 1000ull };

// easy to work with
__int64 startTick, endTick, ticksPerSecond, totalTicks = 0ull;

QueryPerformanceFrequency((LARGE_INTEGER *)&ticksPerSecond);

for (int iter = 0; iter < MAX_ITER; ++iter) {// start looping
    QueryPerformanceCounter((LARGE_INTEGER *)&startTick); // Get start tick
    // code to be timed
    std::cout << "cur_tick = " << iter << "\n";
    QueryPerformanceCounter((LARGE_INTEGER *)&endTick); // Get end tick
    totalTicks += endTick - startTick; // accumulate time taken
}

// convert to elapsed microseconds
__int64 totalMicroSeconds =  (totalTicks * 1000000ull)/ ticksPerSecond;

__int64 hours = totalMicroSeconds / us_per_hour;
totalMicroSeconds %= us_per_hour;
__int64 minutes = totalMicroSeconds / us_per_min;
totalMicroSeconds %= us_per_min;
__int64 seconds = totalMicroSeconds / us_per_sec;
totalMicroSeconds %= us_per_sec;
__int64 milliseconds = totalMicroSeconds / us_per_ms;
totalMicroSeconds %= us_per_ms;


std::cout << "Total time: " << hours << "h ";
std::cout << minutes << "m " << seconds << "s " << milliseconds << "ms ";
std::cout << totalMicroSeconds << "us\n";

return 0;
}
2
pmw1234

Je suggère d'utiliser les fonctions standard de la bibliothèque pour obtenir des informations sur l'heure du système.

Si vous voulez une résolution plus fine, effectuez plus d'itérations d'exécution. Au lieu d'exécuter le programme une fois et d'obtenir des échantillons, exécutez-le 1000 fois ou plus .

2
Thomas Matthews

Si vous voulez obtenir de bons résultats exacts, comme indiqué ci-dessus, votre temps d'exécution dépend de la planification des threads. Une solution complète sans faille à cela, qui devrait donner exactement les mêmes temps pour chaque test, consiste à compiler votre programme pour qu’il soit indépendant du système d’exploitation et à démarrer votre ordinateur afin de l’exécuter dans un environnement sans système d’exploitation. Cependant, une bonne solution consiste simplement à définir l'affinité du thread actuel sur 1 cœur et la priorité sur la priorité la plus élevée. Le résultat de ceci est des résultats très cohérents. De plus, vous devez désactiver les optimisations, ce qui pour g ++ ou gcc signifie l’ajout de -O0 à la ligne de commande, afin d’empêcher l’optimisation de votre code testé. Voici un exemple de la façon dont je compare les fonctions de la racine carrée sur un ordinateur Windows.

#include <iostream>
#include <iomanip>
#include <cstdio>
#include <chrono>
#include <cmath>
#include <windows.h>
#include <intrin.h>
#pragma intrinsic(__rdtsc)

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

unsigned int guess_sqrt32(register unsigned int n) {
    register unsigned int g = 0x8000;
    if(g*g > n) {
        g ^= 0x8000;
    }
    g |= 0x4000;
    if(g*g > n) {
        g ^= 0x4000;
    }
    g |= 0x2000;
    if(g*g > n) {
        g ^= 0x2000;
    }
    g |= 0x1000;
    if(g*g > n) {
        g ^= 0x1000;
    }
    g |= 0x0800;
    if(g*g > n) {
        g ^= 0x0800;
    }
    g |= 0x0400;
    if(g*g > n) {
        g ^= 0x0400;
    }
    g |= 0x0200;
    if(g*g > n) {
        g ^= 0x0200;
    }
    g |= 0x0100;
    if(g*g > n) {
        g ^= 0x0100;
    }
    g |= 0x0080;
    if(g*g > n) {
        g ^= 0x0080;
    }
    g |= 0x0040;
    if(g*g > n) {
        g ^= 0x0040;
    }
    g |= 0x0020;
    if(g*g > n) {
        g ^= 0x0020;
    }
    g |= 0x0010;
    if(g*g > n) {
        g ^= 0x0010;
    }
    g |= 0x0008;
    if(g*g > n) {
        g ^= 0x0008;
    }
    g |= 0x0004;
    if(g*g > n) {
        g ^= 0x0004;
    }
    g |= 0x0002;
    if(g*g > n) {
        g ^= 0x0002;
    }
    g |= 0x0001;
    if(g*g > n) {
        g ^= 0x0001;
    }
    return g;
}

unsigned int empty_function( unsigned int _input ) {
    return _input;
}

unsigned long long empty_ticks=0;
double empty_seconds=0;
Timer my_time;

template<unsigned int benchmark_repetitions>
void benchmark( char* function_name, auto (*function_to_do)( auto ) ) {
    register unsigned int i=benchmark_repetitions;
    register unsigned long long start=0;
    my_time.reset();
    start=__rdtsc();
    while ( i-- ) {
        (*function_to_do)( i << 7 );
    }
    if ( function_name == nullptr ) {
        empty_ticks = (__rdtsc()-start);
        empty_seconds = my_time.elapsed();
        std::cout<< "Empty:\n" << empty_ticks
              << " ticks\n" << benchmark_repetitions << " repetitions\n"
               << std::setprecision(15) << empty_seconds
                << " seconds\n\n";
    } else {
        std::cout<< function_name<<":\n" << (__rdtsc()-start-empty_ticks)
              << " ticks\n" << benchmark_repetitions << " repetitions\n"
               << std::setprecision(15) << (my_time.elapsed()-empty_seconds)
                << " seconds\n\n";
    }
}


int main( void ) {
    void* Cur_Thread=   GetCurrentThread();
    void* Cur_Process=  GetCurrentProcess();
    unsigned long long  Current_Affinity;
    unsigned long long  System_Affinity;
    unsigned long long furthest_affinity;
    unsigned long long nearest_affinity;

    if( ! SetThreadPriority(Cur_Thread,THREAD_PRIORITY_TIME_CRITICAL) ) {
        SetThreadPriority( Cur_Thread, THREAD_PRIORITY_HIGHEST );
    }
    if( ! SetPriorityClass(Cur_Process,REALTIME_PRIORITY_CLASS) ) {
        SetPriorityClass( Cur_Process, HIGH_PRIORITY_CLASS );
    }
    GetProcessAffinityMask( Cur_Process, &Current_Affinity, &System_Affinity );
    furthest_affinity = 0x8000000000000000ULL>>__builtin_clzll(Current_Affinity);
    nearest_affinity  = 0x0000000000000001ULL<<__builtin_ctzll(Current_Affinity);
    SetProcessAffinityMask( Cur_Process, furthest_affinity );
    SetThreadAffinityMask( Cur_Thread, furthest_affinity );

    const int repetitions=524288;

    benchmark<repetitions>( nullptr, empty_function );
    benchmark<repetitions>( "Standard Square Root", standard_sqrt );
    benchmark<repetitions>( "Original Guess Square Root", original_guess_sqrt32 );
    benchmark<repetitions>( "New Guess Square Root", new_guess_sqrt32 );


    SetThreadPriority( Cur_Thread, THREAD_PRIORITY_IDLE );
    SetPriorityClass( Cur_Process, IDLE_PRIORITY_CLASS );
    SetProcessAffinityMask( Cur_Process, nearest_affinity );
    SetThreadAffinityMask( Cur_Thread, nearest_affinity );
    for (;;) { getchar(); }

    return 0;
}

Merci également à Mike Jarvis pour son minuteur.

Et, notez (ceci est très important) que si vous allez exécuter des extraits de code plus volumineux, vous devez VRAIMENT réduire le nombre d’itérations pour empêcher votre ordinateur de se figer.

2
Jack Giffin

Il est préférable d’exécuter plusieurs fois la boucle interne avec la synchronisation de performance une seule fois et moyenne en divisant les répétitions de boucle interne plutôt que d’exécuter le tout (boucle + synchronisation de performance) plusieurs fois et en moyenne. Cela réduira les frais généraux du code de synchronisation des performances par rapport à votre section profilée réelle.

Enveloppez vos appels de la minuterie pour le système approprié. Pour Windows, QueryPerformanceCounter est assez rapide et "sûr" à utiliser.

Vous pouvez également utiliser "rdtsc" sur n'importe quel PC X86 moderne, mais des problèmes peuvent survenir sur certaines machines multicœurs (le "core hopping" peut changer le minuteur) ou si vous avez activé speed-step.

2
Adisak

juste une classe simple qui compare le codeblock:

using namespace std::chrono;

class benchmark {
  public:
  time_point<high_resolution_clock>  t0, t1;
  unsigned int *d;
  benchmark(unsigned int *res) : d(res) { 
                 t0 = high_resolution_clock::now();
  }
  ~benchmark() { t1 = high_resolution_clock::now();
                  milliseconds dur = duration_cast<milliseconds>(t1 - t0);
                  *d = dur.count();
  }
};
// simple usage 
// unsigned int t;
// { // put the code in a block
//  benchmark bench(&t);
//  // ...
//  // code to benchmark
// }
// HERE the t contains time in milliseconds

// one way to use it can be :
#define BENCH(TITLE,CODEBLOCK) \
  unsigned int __time__##__LINE__ = 0;  \
  { benchmark bench(&__time__##__LINE__); \
      CODEBLOCK \
  } \
  printf("%s took %d ms\n",(TITLE),__time__##__LINE__);


int main(void) {
  BENCH("TITLE",{
    for(int n = 0; n < testcount; n++ )
      int a = n % 3;
  });
  return 0;
}
1
nullqube

J'ai créé un lambda qui vous appelle fonction appelez N fois et vous renvoie la moyenne.

double c = BENCHMARK_CNT(25, fillVectorDeque(variable));

Vous pouvez trouver l'en-tête c ++ 11 ici .

0
burner

Dans les cas où vous souhaitez synchroniser le même bout de code chaque fois qu'il est exécuté (par exemple, pour profiler du code que vous pensez être un goulet d'étranglement), voici une enveloppe (une légère modification) de la fonction d'Andreas Bonini que je trouve utile:

#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#endif

/*
 *  A simple timer class to see how long a piece of code takes. 
 *  Usage:
 *
 *  {
 *      static Timer timer("name");
 *
 *      ...
 *
 *      timer.start()
 *      [ The code you want timed ]
 *      timer.stop()
 *
 *      ...
 *  }
 *
 *  At the end of execution, you will get output:
 *
 *  Time for name: XXX seconds
 */
class Timer
{
public:
    Timer(std::string name, bool start_running=false) : 
        _name(name), _accum(0), _running(false)
    {
        if (start_running) start();
    }

    ~Timer() { stop(); report(); }

    void start() {
        if (!_running) {
            _start_time = GetTimeMicroseconds();
            _running = true;
        }
    }
    void stop() {
        if (_running) {
            unsigned long long stop_time = GetTimeMicroseconds();
            _accum += stop_time - _start_time;
            _running = false;
        }
    }
    void report() { 
        std::cout<<"Time for "<<_name<<": " << _accum / 1.e6 << " seconds\n"; 
    }
private:
    // cf. http://stackoverflow.com/questions/1861294/how-to-calculate-execution-time-of-a-code-snippet-in-c
    unsigned long long GetTimeMicroseconds()
    {
#ifdef _WIN32
        /* Windows */
        FILETIME ft;
        LARGE_INTEGER li;

        /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
         *   * to a LARGE_INTEGER structure. */
        GetSystemTimeAsFileTime(&ft);
        li.LowPart = ft.dwLowDateTime;
        li.HighPart = ft.dwHighDateTime;

        unsigned long long ret = li.QuadPart;
        ret -= 116444736000000000LL; /* Convert from file time to UNIX Epoch time. */
        ret /= 10; /* From 100 nano seconds (10^-7) to 1 microsecond (10^-6) intervals */
#else
        /* Linux */
        struct timeval tv;

        gettimeofday(&tv, NULL);

        unsigned long long ret = tv.tv_usec;
        /* Adds the seconds (10^0) after converting them to microseconds (10^-6) */
        ret += (tv.tv_sec * 1000000);
#endif
        return ret;
    }
    std::string _name;
    long long _accum;
    unsigned long long _start_time;
    bool _running;
};
0
Mike Jarvis

J'ai créé un utilitaire simple pour mesurer les performances de blocs de code, en utilisant le paramètre high_resolution_clock de la bibliothèque chrono: https://github.com/nfergu/codetimer .

Les timings peuvent être enregistrés avec différentes clés et une vue agrégée des timings de chaque clé peut être affichée.

L'utilisation est la suivante:

#include <chrono>
#include <iostream>
#include "codetimer.h"

int main () {
    auto start = std::chrono::high_resolution_clock::now();
    // some code here
    CodeTimer::record("mykey", start);
    CodeTimer::printStats();
    return 0;
}
0
Neil

Vous pouvez également consulter le [cxx-rtimers][1] sur GitHub, qui fournit des routines utilisant uniquement des en-têtes pour collecter des statistiques sur l'exécution d'un bloc de code dans lequel vous pouvez créer une variable locale. Ces timers ont des versions qui utilisent std :: chrono sur C++ 11, ou des timers de la bibliothèque Boost, ou des fonctions de timer POSIX standard. Ces minuteries indiqueront la durée moyenne, maximale et minimale passée dans une fonction, ainsi que le nombre d'appels de cette fonction. Ils peuvent être utilisés simplement comme suit:

#include <rtimers/cxx11.hpp>

void expensiveFunction() {
    static rtimers::cxx11::DefaultTimer timer("expensive");
    auto scopedStartStop = timer.scopedStart();
    // Do something costly...
}
0
rwp

boost :: timer vous donnera probablement autant de précision que nécessaire. Il est loin d'être assez précis pour vous dire combien de temps va prendre a = a+1;, mais quelle raison auriez-vous besoin de chronométrer quelque chose qui prend quelques nanosecondes?

0
Brendan Long