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

121

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

J'utilise le code suivant pour ce 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 les petites entrées ou les déclarations courtes telles que a = a + 1, j'obtiens le résultat "0 seconde". Je pense que ça doit être quelque chose comme 0,0000001 seconde ou quelque chose comme ça.

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

Avez-vous une solution?

AhmetB - Google
la source
29
Gardez à l'esprit que toute comparaison basée sur le décalage horaire peut très bien être inexacte en raison du fait que le système d'exploitation peut ne pas exécuter votre thread du début à la fin. Il peut l'interrompre et exécuter d'autres threads entrelacés avec le vôtre, ce qui aura un impact significatif sur le temps réel nécessaire pour terminer votre opération. Vous pouvez exécuter plusieurs fois et faire la moyenne des résultats; vous pouvez réduire le nombre d'autres processus en cours d'exécution. Mais aucun de ceux-ci n'éliminera complètement l'effet de suspension du fil.
Mordachai
14
Mordachi, pourquoi voudriez-vous l'éliminer? Vous voulez voir comment votre fonction fonctionne dans un environnement réel, pas dans un royaume magique où les threads ne sont jamais interrompus. Tant que vous l'exécutez plusieurs fois et que vous faites une moyenne, ce sera très précis.
Thomas Bonini
Oui, je l'exécute plusieurs fois et obtient des résultats moyens.
AhmetB - Google
14
Andreas, le commentaire de Mordachai est pertinent si l'OP souhaite comparer les performances de son code à un algorithme différent. Par exemple, s'il exécute plusieurs tests d'horloge cet après-midi, puis teste un algorithme différent demain matin, sa comparaison peut ne pas être fiable car il peut partager des ressources avec beaucoup plus de processus l'après-midi que le matin. Ou peut-être qu'un ensemble de code entraînera le système d'exploitation pour lui donner moins de temps de traitement. Il existe de nombreuses raisons pour lesquelles ce type de mesure du rendement n'est pas fiable s'il souhaite effectuer une comparaison temporelle.
weberc2
4
@Mordachai Je sais que je réponds à un vieux commentaire, mais pour quiconque trébuche là-dessus comme je l'ai fait - pour chronométrer les performances des algorithmes, vous voulez prendre le minimum de quelques courses, pas la moyenne. C'est celui qui a eu le moins d'interruptions de la part du système d'exploitation et qui synchronise principalement votre code.
Baruch

Réponses:

115

Vous pouvez utiliser cette fonction que j'ai écrite. Vous appelez GetTimeMs64(), et il renvoie le nombre de millisecondes écoulées depuis l'époque Unix en utilisant l'horloge système - tout comme time(NULL), sauf en millisecondes.

Cela fonctionne à la fois sur Windows et Linux; il est sans fil.

Notez que la granularité est de 15 ms sur windows; sous linux, cela dépend de l'implémentation, mais généralement aussi 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
}
Thomas Bonini
la source
1
Pour référence future: je le jette simplement dans un fichier d'en-tête et je l'utilise. Heureux de l'avoir.
Daniel Handojo
1
Je pense que la méthode gettimeofdaypeut donner un résultat inattendu si l'horloge système est modifiée. Si cela pose un problème pour vous, vous voudrez peut-être regarder à la clock_gettimeplace.
Azmisov
Cette méthode pour Windows présente-t-elle des avantages par rapport à GetTickCount?
MicroVirus
Ne compile pas avecgcc -std=c99
Assimilater
@MicroVirus: oui, GetTickCountc'est le temps écoulé depuis le démarrage du système, alors que ma fonction renvoie le temps depuis l'époque UNIX ce qui signifie que vous pouvez l'utiliser pour les dates et les heures. Si vous n'êtes intéressé que par le temps écoulé entre deux événements, le mien est toujours un meilleur choix car c'est un int64; GetTickCount est un int32 et déborde tous les 50 jours, ce qui signifie que vous pouvez obtenir des résultats étranges si les deux événements que vous avez enregistrés se trouvent entre le débordement.
Thomas Bonini
43

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

arhuaco
la source
5
Vous devriez ajouter #include <sys/time.h>au début de votre exemple.
niekas
40

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

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

gongzhitaao
la source
/usr/lib/x86_64-linux-gnu/libstdc++.so.6: version GLIBCXX_3.4.19 not found (required by ../cpu_2d/g500)
J'obtiens
@julianromera quelle plateforme utilisez-vous? avez-vous installé la bibliothèque libstdc ++ et g ++?
gongzhitaao
C'est une grille Slurm de Linux ubuntu 12. Je viens de le faire réparer. J'ai ajouté -static-libstdc ++ à la fin de l'éditeur de liens. Merci d'avoir demandé @gongzhitaao
user9869932
18
#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;
}

Lorsqu'il progress_timerest hors de portée, il affichera le temps écoulé depuis sa création.

MISE À JOUR : Voici une version qui fonctionne sans Boost (testé sur macOS / iOS):

#include <chrono>
#include <string>
#include <iostream>
#include <math.h>
#include <unistd.h>

class NLTimerScoped {
private:
    const std::chrono::steady_clock::time_point start;
    const std::string name;

public:
    NLTimerScoped( const std::string & name ) : name( name ), start( std::chrono::steady_clock::now() ) {
    }


    ~NLTimerScoped() {
        const auto end(std::chrono::steady_clock::now());
        const auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>( end - start ).count();

        std::cout << name << " duration: " << duration_ms << "ms" << std::endl;
    }

};

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

    {
        NLTimerScoped timer( "sin sum" );

        float a = 0.0f;

        for ( int i=0; i < 1000000; i++ ) {
            a += sin( (float) i / 100 );
        }

        std::cout << "sin sum = " << a << std::endl;
    }



    {
        NLTimerScoped timer( "sleep( 4 )" );

        sleep( 4 );
    }



    return 0;
}
Tomas Andrle
la source
2
Cela fonctionne, mais notez que progress_timer est obsolète (quelque temps avant boost 1.50) - auto_cpu_timer peut être plus approprié.
davidA
3
@meowsqueak hmm, auto_cpu_timer semble exiger que la bibliothèque système Boost soit liée, donc ce n'est plus une solution d'en-tête uniquement. Dommage ... rend les autres options plus attrayantes tout d'un coup.
Tomas Andrle
1
oui, c'est un bon point, si vous ne liez pas déjà Boost, c'est plus de problèmes que cela ne vaut la peine. Mais si vous le faites déjà, cela fonctionne très bien.
davidA
@meowsqueak Ouais, ou pour quelques tests de référence rapides, procurez-vous simplement cette ancienne version de Boost.
Tomas Andrle
@TomasAndrle Le lien n'existe plus.
Zheng Qu
5

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

Capitaine Comic
la source
Mais l'utilisation de windows.h est restreinte. La même source compilée doit fonctionner à la fois sous Windows et Unix. Comment gérer ce problème?
AhmetB - Google
2
Ensuite, recherchez une bibliothèque de wrapper stackoverflow.com/questions/1487695/…
Captain Comic
4
la même source compilée ressemble à ce que vous voulez exécuter le même binaire sur les deux systèmes, ce qui ne semble pas être le cas. si vous vouliez dire la même source alors un #ifdefdoit être ok (et il est en juger par la réponse que vous avez accepté), et je ne vois pas le problème: #ifdef WIN32 #include <windows.h> ... #else ... #endif.
juste quelqu'un
3

Dans certains programmes que j'ai écrits, j'ai utilisé RDTS à cette fin. RDTSC ne concerne pas le temps mais le nombre de cycles depuis le démarrage du processeur. Il faut le calibrer sur votre système pour obtenir un résultat en seconde, mais c'est vraiment pratique quand on veut évaluer les performances, c'est encore mieux d'utiliser directement le nombre de cycles sans essayer de les remettre en secondes.

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

Kriss
la source
2

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

Si vous souhaitez 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 1 000 fois ou plus.

Thomas Matthews
la source
2

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

Enveloppez vos appels de 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 il peut y avoir des problèmes sur certaines machines multicœurs (le saut de cœur peut changer la minuterie) ou si vous avez activé le pas de vitesse.

Adisak
la source
2

(solution spécifique à Windows) La façon actuelle (vers 2017) d'obtenir des horaires précis sous Windows est d'utiliser "QueryPerformanceCounter". Cette approche a l'avantage de donner des résultats très précis et est recommandée par MS. Plop simplement le blob de code dans une nouvelle application console pour obtenir un exemple fonctionnel. 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;
}

la source
2

Une solution complète et sans faille à la planification des threads, 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 d'exécuter le programme dans un environnement sans OS. Pourtant, cela est largement impraticable et serait au mieux difficile.

Un bon substitut à la suppression du système d'exploitation consiste simplement à définir l'affinité du thread actuel sur 1 cœur et la priorité sur la plus élevée. Cette alternative devrait fournir des résultats suffisamment cohérents.

Vous devez également désactiver les optimisations qui interféreraient avec le débogage, ce qui pour g ++ ou gcc signifie ajouter -Ogà la ligne de commande , pour éviter que le code testé ne soit optimisé. Le -O0drapeau ne doit pas être utilisé car il introduit une surcharge inutile supplémentaire qui serait incluse dans les résultats de synchronisation, faussant ainsi la vitesse chronométrée du code.

Au contraire, à la fois en supposant que vous utilisez -Ofast(ou, à tout le moins, -O3) sur la version de production finale et en ignorant le problème de l'élimination du code "mort", -Ogeffectue très peu d'optimisations par rapport à -Ofast; -Ogpeut ainsi dénaturer la vitesse réelle du code dans le produit final.

En outre, tous les tests de vitesse (dans une certaine mesure) parjure: dans le produit de production final compilé avec -Ofast, chaque extrait / section / fonction de code n'est pas isolé; au contraire, chaque extrait de code s'écoule en continu dans le suivant, permettant ainsi au compilateur de potentiellement joindre, fusionner et optimiser ensemble des morceaux de code de partout.

Dans le même temps, si vous comparez un extrait de code qui fait un usage realloc()intensif, alors l'extrait de code peut s'exécuter plus lentement dans un produit de production avec une fragmentation de mémoire suffisamment élevée. Par conséquent, l'expression «le tout est plus que la somme de ses parties» s'applique à cette situation car le code de la version de production finale peut s'exécuter sensiblement plus rapidement ou plus lentement que l'extrait de code individuel que vous testez en vitesse.

Une solution partielle qui peut réduire l'incongruité consiste à utiliser -Ofastpour les tests de vitesse AVEC l'ajout de asm volatile("" :: "r"(var))aux variables impliquées dans le test pour empêcher l'élimination du code mort / boucle.

Voici un exemple de comparaison des fonctions de racine carrée sur un ordinateur Windows.

// set USE_ASM_TO_PREVENT_ELIMINATION  to 0 to prevent `asm volatile("" :: "r"(var))`
// set USE_ASM_TO_PREVENT_ELIMINATION  to 1 to enforce `asm volatile("" :: "r"(var))`
#define USE_ASM_TO_PREVENT_ELIMINATION 1

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

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-- ) {
        auto result = (*function_to_do)( i << 7 );
        #if USE_ASM_TO_PREVENT_ELIMINATION == 1
            asm volatile("" :: "r"(
                // There is no data type in C++ that is smaller than a char, so it will
                //  not throw a segmentation fault error to reinterpret any arbitrary
                //  data type as a char. Although, the compiler might not like it.
                result
            ));
        #endif
    }
    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;
}

Aussi, merci à Mike Jarvis pour son chronomètre.

Veuillez noter (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 éviter que votre ordinateur ne se fige.

Jack Giffin
la source
2
Bonne réponse sauf pour désactiver l'optimisation. L'analyse comparative du -O0code est une grosse perte de temps car la surcharge -O0 au lieu d'une normale -O2ou -O3 -march=nativevarie énormément en fonction du code et de la charge de travail. par exemple, les variables tmp nommées supplémentaires coûtent du temps à -O0. Il existe d'autres moyens d'éviter que les choses ne s'optimisent, comme cacher des choses à l'optimiseur avec volatiledes fonctions non en ligne ou des instructions asm en ligne vides. -O0n'est même pas presque utilisable car le code a des goulots d'étranglement différents-O0 , pas les mêmes mais pires.
Peter Cordes
1
Ugh, ce -Ogn'est toujours pas très réaliste, selon le code. Au moins -O2, de préférence -O3est plus réaliste. Utilisez asm volatile("" ::: "+r"(var))ou quelque chose pour que le compilateur matérialise une valeur dans un registre et empêche la propagation constante à travers celui-ci.
Peter Cordes
@PeterCordes Merci encore pour vos idées. J'ai mis à jour le contenu avec -O3et l'extrait de code avec asm volatile("" ::: "+r"(var)).
Jack Giffin
1
asm volatile("" ::: "+r"( i ));semble inutile. Dans le code optimisé, il n'y a aucune raison de forcer le compilateur à se matérialiser iaussi bien qu'à l' i<<7intérieur de la boucle. Vous l'empêchez d'optimiser au tmp -= 128lieu de changer à chaque fois. L'utilisation du résultat d'un appel de fonction est cependant bonne, si ce n'est pas le cas void. Comme int result = (*function_to_do)( i << 7 );. Vous pouvez utiliser une asmdéclaration sur ce résultat.
Peter Cordes
@PeterCordes Merci encore beaucoup ou vos idées. Mon message contient maintenant les corrections pour la valeur de retour de function_to_doafin qu'elle function_to_dopuisse être incorporée sans être éliminée. Veuillez me faire savoir si vous avez d'autres suggestions.
Jack Giffin
1

Pour les cas où vous souhaitez chronométrer le même bout de code à chaque fois qu'il est exécuté (par exemple pour le profilage de code que vous pensez être un goulot d'étranglement), voici un wrapper autour (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;
};
Mike Jarvis
la source
1

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;
}
nullqube
la source
0

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

Brendan Long
la source
Il s'appuie sur la clock()fonction de l'en-tête standard C ++.
Petter
0

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

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

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

brûleur
la source
0

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

Les minutages peuvent être enregistrés par rapport à différentes touches et une vue agrégée des minutages pour 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;
}
Neil
la source
0

Vous pouvez également consulter le [cxx-rtimers][1]sur GitHub, qui fournit des routines d'en-tête uniquement pour collecter des statistiques sur l'exécution de tout bloc de code où vous pouvez créer une variable locale. Ces minuteries ont des versions qui utilisent std :: chrono sur C ++ 11, ou des minuteries de la bibliothèque Boost, ou des fonctions de minuterie POSIX standard. Ces minuteries indiqueront la durée moyenne, maximale et minimale passée dans une fonction, ainsi que le nombre de fois qu'elle est appelée. Ils peuvent être utilisés de la manière suivante:

#include <rtimers/cxx11.hpp>

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

C'est comme ça que je fais, peu de code, facile à comprendre, correspond à mes besoins:

void bench(std::function<void()> fnBench, std::string name, size_t iterations)
{
    if (iterations == 0)
        return;
    if (fnBench == nullptr)
        return;
    std::chrono::high_resolution_clock::time_point start, end;
    if (iterations == 1)
    {
        start = std::chrono::high_resolution_clock::now();
        fnBench();
        end = std::chrono::high_resolution_clock::now();
    }
    else
    {
        start = std::chrono::high_resolution_clock::now();
        for (size_t i = 0; i < iterations; ++i)
            fnBench();
        end = std::chrono::high_resolution_clock::now();
    }
    printf
    (
        "bench(*, \"%s\", %u) = %4.6lfs\r\n",
        name.c_str(),
        iterations,
        std::chrono::duration_cast<std::chrono::duration<double>>(end - start).count()
    );
}

Usage:

bench
(
    []() -> void // function
    {
        // Put your code here
    },
    "the name of this", // name
    1000000 // iterations
);
cisco211
la source
0
#include <omp.h>

double start = omp_get_wtime();

// code 

double finish = omp_get_wtime();

double total_time = finish - start;
Nate Frisch
la source
2
Bien que ce code puisse résoudre la question, inclure une explication sur comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre publication et entraînerait probablement plus de votes à la hausse. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant. Veuillez modifier votre réponse pour ajouter des explications et donner une indication des limites et des hypothèses applicables.
Dharman