Как рассчитать время выполнения фрагмента кода в C++

Я должен вычислить время выполнения фрагмента кода C++ в секундах. Он должен работать на компьютерах с Windows или Unix.

Я использую код следующий код, чтобы сделать это. (импорт до)

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() функция С ++.

У тебя есть решение?

16 ответов

Решение

Вы можете использовать эту функцию, которую я написал. Ты звонишь GetTimeMs64()и возвращает количество миллисекунд, прошедших с эпохи Unix с использованием системных часов - так же, как time(NULL)кроме как в миллисекундах.

Работает как на Windows, так и на Linux; это потокобезопасно.

Обратите внимание, что гранулярность на окнах составляет 15 мс; в Linux это зависит от реализации, но обычно это также 15 мс.

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

У меня есть еще один рабочий пример, который использует микросекунды (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

Вот простое решение в 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

#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 выходит из области видимости, он распечатает время, прошедшее с момента его создания.

ОБНОВЛЕНИЕ: я сделал простую автономную замену (OSX/iOS, но легко портировать): https://github.com/catnapgames/TestTimerScoped

Windows предоставляет функцию QueryPerformanceCounter(), а Unix имеет функцию gettimeofday(). Обе функции могут измерять разницу не менее 1 микросекунды.

В некоторых программах, которые я написал, я использовал RDTS для этой цели. RDTSC не о времени, а о количестве циклов от запуска процессора. Вы должны откалибровать его в своей системе, чтобы получить результат за секунду, но это действительно удобно, когда вы хотите оценить производительность, еще лучше использовать количество циклов напрямую, не пытаясь изменить их обратно на секунды.

(ссылка выше на страницу французской википедии, но там есть примеры кода на C++, английская версия здесь)

(решение для Windows) Текущий (около 2017 года) способ получить точные значения времени под окнами - это использовать "QueryPerformanceCounter". Преимущество этого подхода состоит в том, что он дает очень точные результаты и рекомендуется MS. Просто добавьте блок кода в новое консольное приложение, чтобы получить рабочий образец. Здесь идет длинное обсуждение: получение меток времени с высоким разрешением

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

Я предлагаю использовать стандартные библиотечные функции для получения информации о времени из системы.

Если вы хотите более точное разрешение, выполните больше итераций выполнения. Вместо того, чтобы запускать программу один раз и получать образцы, запустите ее 1000 и более раз.

Лучше запускать внутренний цикл несколько раз с синхронизацией производительности только один раз и в среднем путем деления повторений внутреннего цикла, чем выполнять весь цикл (цикл + синхронизация производительности) несколько раз и в среднем. Это уменьшит накладные расходы на код синхронизации производительности по сравнению с вашим фактическим профилированным разделом.

Оберните ваши звонки по таймеру для соответствующей системы. Для Windows QueryPerformanceCounter довольно быстрый и "безопасный" в использовании.

Вы также можете использовать "rdtsc" на любом современном ПК с архитектурой X86, но могут быть проблемы на некоторых многоядерных машинах (скачкообразная перестройка ядра может изменить таймер) или если у вас включен какой-то скоростной шаг.

Если вы хотите получить точные точные результаты, то, как указано выше, время выполнения зависит от планирования потоков. Полное надежное решение этой проблемы, которое должно давать одинаковое время для каждого теста, состоит в том, чтобы скомпилировать вашу программу, чтобы она не зависела от ОС, и загрузить компьютер, чтобы запустить программу в среде без ОС. Тем не менее, хорошая замена этому - просто установить сродство текущего потока на 1 ядро ​​и приоритет наивысшего. Результатом этого является очень последовательные результаты. И еще одна вещь - вы должны отключить оптимизацию, что для g++ или gcc означает добавление -O0 в командную строку, чтобы предотвратить оптимизацию тестируемого кода. Вот пример того, как я тестирую функции квадратного корня на компьютере с 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;
}

Кроме того, кредит Майку Джарвису за его Таймер.

И, пожалуйста, обратите внимание (это очень важно), что если вы собираетесь запускать большие фрагменты кода, то вы ДЕЙСТВИТЕЛЬНО должны уменьшить количество итераций, чтобы ваш компьютер не зависал.

Для случаев, когда вы хотите синхронизировать один и тот же фрагмент кода каждый раз, когда он выполняется (например, для профилирования кода, который, по вашему мнению, может быть узким местом), вот обертка (небольшое изменение) функции Андреаса Бонини, которую я считаю полезной:

#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://stackru.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;
};

Просто простой класс, который тестирует кодовый блок:

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

Вы также можете посмотреть на [cxx-rtimers][1] на GitHub, которые предоставляют некоторые подпрограммы только для заголовков для сбора статистики во время выполнения любого кодового блока, где вы можете создать локальную переменную. Эти таймеры имеют версии, которые используют std::chrono на C++11, или таймеры из библиотеки Boost, или стандартные функции таймера POSIX. Эти таймеры сообщат о средней, максимальной и минимальной продолжительности использования функции, а также о количестве ее вызовов. Их можно использовать просто так:

#include <rtimers/cxx11.hpp>

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

boost::timer, вероятно, даст вам столько точности, сколько вам нужно. Это не достаточно точно, чтобы сказать вам, как долго a = a+1; возьму, но у меня, по какой причине вы бы рассчитали время, которое занимает пару наносекунд?

Я создал простую утилиту для измерения производительности блоков кода, используя high_resolution_clock библиотеки 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;
}

Я создал лямбду, которая вызывает вас вызовом функции N раз и возвращает вам среднее значение.

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

Вы можете найти заголовок C++11 здесь.

#include <omp.h>

double start = omp_get_wtime();

// code 

double finish = omp_get_wtime();

double total_time = finish - start;

Вот как я это делаю, мало кода, легко понять, соответствует моим потребностям:

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
);
Другие вопросы по тегам