代码之家  ›  专栏  ›  技术社区  ›  ahmet alp balkan

如何计算C++代码片断的执行时间

  •  113
  • ahmet alp balkan  · 技术社区  · 15 年前

    我使用下面的代码来实现这一点(导入之前)

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

    然而,对于小的输入或简短的语句,例如a=a+1,我会得到“0秒”的结果。我想大概是0.0000001秒左右。

    System.nanoTime() 在Java中,在这种情况下效果非常好。但是,我无法从中获得相同的确切功能 clock() C++的功能。

    16 回复  |  直到 10 年前
        1
  •  116
  •   Andreas Bonini    9 年前

    GetTimeMs64() ,并使用系统时钟返回自unix纪元以来经过的毫秒数-与 time(NULL) ,毫秒除外。

    它可以在windows和linux上工作;它是线程安全的。

    #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
    }
    
        2
  •  43
  •   arhuaco    8 年前

    我有另一个使用微秒的工作示例(UNIX、POSIX等)。

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

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

        3
  •  42
  •   Alex random_user    6 年前

    这里有一个C++11中的简单解决方案,它提供了令人满意的分辨率。

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

    或者在*nix上,对于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_;
    };
    

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

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

        4
  •  18
  •   Tomas Andrle    5 年前
    #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;
    }
    

    什么时候 progress_timer 超出范围,它将打印自创建以来经过的时间。

    :这里有一个不使用Boost的版本(在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;
    }
    
    
        5
  •  5
  •   Captain Comic    15 年前

    Windows提供QueryPerformanceCounter()函数,Unix提供gettimeofday(),这两个函数都可以测量至少1微秒的差异。

        6
  •  3
  •   Carlton Henderson kriss    9 年前

    在我编写的一些程序中,我使用了 RDTS

    here )

        7
  •  2
  •   Thomas Matthews    15 年前

    我建议使用标准库函数从系统中获取时间信息。

        8
  •  2
  •   Adisak    15 年前

    运行内循环几次,性能计时只运行一次,通过划分内循环重复次数来平均,比运行整件事情(循环+性能计时)几次并平均要好。这将减少性能计时代码与实际分析部分的开销。

    将计时器调用包装到适当的系统。对于Windows,QueryPerformanceCounter非常快速且使用“安全”。

    您也可以在任何现代X86 PC上使用“rdtsc”,但在某些多核计算机上可能会出现问题(内核跳变可能会改变计时器),或者如果您启用了某种速度步长。

        9
  •  2
  •   user8991265 user8991265    7 年前

    当前(大约2017年)在windows下获得准确计时的方法是使用“QueryPerformanceCounter”。这种方法的优点是可以给出非常准确的结果,这是MS.推荐的方法。只需将代码块放到一个新的控制台应用程序中,就可以得到一个工作样本。这里有一个冗长的讨论: Acquiring High resolution time stamps

    #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;
    }
    
        10
  •  2
  •   Jack G    3 年前

    免费操作系统的一个很好的替代方法就是将当前线程的亲和性设置为1个核心,并将优先级设置为最高。该替代方案应提供足够一致的结果。

    假设您使用 -Ofast (或者,至少, -O3 )在最终的产品构建中,忽略了“死”代码消除的问题, -Og -过去 ; 因此 -奥格

    此外,所有速度测试(在某种程度上)都是伪证:在最终生产产品中使用 -过去

    同时,如果您正在对大量使用 realloc()

    可以减少不一致性的部分解决方案是使用 对于速度测试,添加 asm volatile("" :: "r"(var)) 测试中涉及的变量,以防止死代码/循环消除。

    下面是一个如何在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;
    }
    

    此外,迈克·贾维斯的计时器也值得称赞。

        11
  •  1
  •   Mike Jarvis    9 年前

    对于每次执行同一段代码时都要对其计时的情况(例如,对于您认为可能是瓶颈的分析代码),我发现Andreas Bonini函数的包装器(对其稍作修改)非常有用:

    #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;
    };
    
        12
  •  1
  •   nullqube    6 年前

    只是一个对代码块进行基准测试的简单类:

    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;
    }
    
        13
  •  0
  •   Brendan Long    14 年前
        14
  •  0
  •   burner    10 年前

    我创建了一个lambda,它调用您函数调用N次并返回平均值。

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

    here .

        15
  •  0
  •   Neil    8 年前

    我使用chrono库的高分辨率时钟创建了一个用于测量代码块性能的简单实用程序: https://github.com/nfergu/codetimer .

    用法如下:

    #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;
    }
    
        16
  •  0
  •   rwp    7 年前

    [cxx-rtimers][1] 在GitHub上,它提供了一些只包含头的例程,用于收集任何代码块运行时的统计信息,您可以在其中创建局部变量。这些计时器的版本使用C++11上的std::chrono,或Boost库中的计时器,或标准POSIX计时器函数。这些计时器将报告平均值、最大值和;函数内花费的最短持续时间,以及调用该函数的次数。它们可以简单地如下使用:

    #include <rtimers/cxx11.hpp>
    
    void expensiveFunction() {
        static rtimers::cxx11::DefaultTimer timer("expensive");
        auto scopedStartStop = timer.scopedStart();
        // Do something costly...
    }
    
        17
  •  0
  •   cisco211    4 年前

    我就是这样做的,代码不多,容易理解,适合我的需要:

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

    用法:

    bench
    (
        []() -> void // function
        {
            // Put your code here
        },
        "the name of this", // name
        1000000 // iterations
    );
    
        18
  •  0
  •   Nate Frisch    4 年前
    #include <omp.h>
    
    double start = omp_get_wtime();
    
    // code 
    
    double finish = omp_get_wtime();
    
    double total_time = finish - start;