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


121

Мне нужно вычислить время выполнения фрагмента кода 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()функции C ++.

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


29
Имейте в виду, что любое сравнение на основе разницы во времени может быть неточным из-за того, что ОС может не запускать ваш поток от начала до конца. Он может прервать его и запустить другие потоки, чередующиеся с вашим, что существенно повлияет на фактическое время, необходимое для завершения вашей операции. Вы можете запускать несколько раз и усреднять результаты; вы можете минимизировать количество других запущенных процессов. Но ни один из них не устранит полностью эффект подвешивания резьбы.
Mordachai

14
Мордачи, зачем тебе его устранять? Вы хотите увидеть, как ваша функция работает в реальной среде, а не в волшебной сфере, где потоки никогда не прерываются. Если вы запустите его несколько раз и получите среднее значение, оно будет очень точным.
Томас Бонини,

Да, я запустил его несколько раз и оценил результаты.
AhmetB - Google,

14
Андреас, комментарий Мордачай уместен, если ОП хочет сравнить производительность своего кода с другим алгоритмом. Например, если он запускает несколько тестов часов сегодня днем, а затем завтра утром тестирует другой алгоритм, его сравнение может быть ненадежным, поскольку днем ​​он может делиться ресурсами с гораздо большим количеством процессов, чем утром. Или, может быть, один набор кода заставит ОС сократить время обработки. Существует множество причин, по которым этот тип измерения производительности ненадежен, если он хочет выполнить сравнение по времени.
weberc2

4
@Mordachai Я знаю, что отвечаю на старый комментарий, но для тех, кто наткнется на это, как и я, - чтобы рассчитать производительность алгоритмов, которые вы хотите выполнить как минимум из нескольких прогонов, а не в среднем. Это тот, у которого меньше всего прерываний со стороны ОС, и поэтому он в основном синхронизирует ваш код.
Барух

Ответы:


115

Вы можете использовать эту функцию, которую я написал. Вы вызываете 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
}

1
Для справки в будущем: я просто добавляю его в файл заголовка и использую. Рад это иметь.
Даниэль Хандоджо,

1
Я считаю, что этот метод gettimeofdayможет дать непредвиденный результат при изменении системных часов. Если это будет проблемой для вас, вы можете clock_gettimeвместо этого взглянуть .
Азмисов 09

Есть ли преимущества у этого метода для Windows GetTickCount?
MicroVirus

Не компилируется с использованиемgcc -std=c99
Assimilater

@MicroVirus: да, GetTickCountэто время, прошедшее с момента запуска системы, а моя функция возвращает время с эпохи UNIX, что означает, что вы можете использовать ее для даты и времени. Если вас интересует только время, прошедшее между двумя событиями, мой вариант все равно будет лучшим выбором, потому что это int64; GetTickCount - это тип int32, который переполняется каждые 50 дней, что означает, что вы можете получить странные результаты, если два зарегистрированных вами события находятся между переполнением.
Томас Бонини

43

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


5
Вы должны добавить #include <sys/time.h>в начале вашего примера.
niekas

40

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


Я получаю эту ошибку с вашим решением на С ++ 11:/usr/lib/x86_64-linux-gnu/libstdc++.so.6: version GLIBCXX_3.4.19 not found (required by ../cpu_2d/g500)
user9869932 02

@julianromera, какую платформу вы используете? вы установили библиотеку libstdc ++ и g ++?
gongzhitaao 02

Это сетка Slurm Linux ubuntu 12. Я только что исправил. Я добавил -static-libstdc ++ в конец компоновщика. Спасибо за вопрос @gongzhitaao
user9869932 02

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

Когда он 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;
}

2
Это работает, но обратите внимание, что progress_timer устарел (когда-то до boost 1.50) - auto_cpu_timer может быть более подходящим.
davidA

3
@meowsqueak хм, auto_cpu_timer, похоже, требует, чтобы системная библиотека Boost была связана, поэтому это больше не решение только для заголовков. Жаль ... Внезапно другие варианты становятся более привлекательными.
Tomas Andrle

1
да, это хороший момент, если вы еще не связали Boost, тогда это больше проблем, чем того стоит. Но если вы уже это делаете, это работает довольно хорошо.
davidA 01

@meowsqueak Да, или для быстрых тестов производительности просто скачайте старую версию Boost.
Tomas Andrle

@TomasAndrle Ссылка больше не существует.
Чжэн Цюй

5

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


Но использование windows.h ограничено. Один и тот же скомпилированный исходный код должен работать как в Windows, так и в Unix. Как справиться с этой проблемой?
AhmetB - Google,

2
Тогда поищите некоторую библиотеку-оболочку stackoverflow.com/questions/1487695/…
Captain Comic

4
один и тот же скомпилированный исходный код звучит так, как будто вы хотите запустить один и тот же двоичный файл в обеих системах, что, похоже, не так. если вы имели в виду один и тот же источник затем #ifdefдолжен быть в порядке (и это , судя по ответу вы приняли), и тогда я не вижу проблемы: #ifdef WIN32 #include <windows.h> ... #else ... #endif.
просто кто-нибудь

3

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

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


2

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

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


2

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

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

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


2

(решение для 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;
}

2

Полное безошибочное решение для планирования потоков, которое должно давать одинаковое время для каждого теста, - это скомпилировать вашу программу так, чтобы она не зависела от ОС, и загрузила компьютер, чтобы запустить программу в среде, свободной от ОС. Тем не менее, это в значительной степени непрактично и в лучшем случае будет затруднительно.

Хорошая замена отказу от ОС - просто установить привязку текущего потока к 1 ядру, а приоритет - к наивысшему. Эта альтернатива должна обеспечивать достаточно последовательные результаты.

Также вам следует отключить оптимизацию, которая может мешать отладке, что для g ++ или gcc означает добавление -Ogв командную строку , чтобы предотвратить оптимизацию тестируемого кода. -O0Флаг не следует использовать , поскольку он вводит дополнительные ненужные накладные расходы , которые будут включены в результатах синхронизации, тем самым искажая таймерную скорость кода.

Напротив, оба предположения, что вы используете -Ofast(или, по крайней мере, -O3) в окончательной производственной сборке, и игнорирование проблемы удаления «мертвого» кода, -Ogвыполняют очень мало оптимизаций по сравнению с -Ofast; таким образом -Ogможет искажать реальную скорость кода в конечном продукте.

Кроме того, все тесты скорости (до некоторой степени) лжесвидетельствуют: в конечном продукте, скомпилированном с помощью -Ofast, каждый фрагмент / раздел / функция кода не изолирован; скорее, каждый фрагмент кода непрерывно перетекает в следующий, что позволяет компилятору потенциально объединять, объединять и оптимизировать фрагменты кода из любого места.

В то же время, если вы тестируете фрагмент кода, который интенсивно использует realloc(), то этот фрагмент кода может работать медленнее в производственном продукте с достаточно высокой фрагментацией памяти. Следовательно, выражение «целое - это больше, чем сумма его частей» применимо к этой ситуации, потому что код в окончательной производственной сборке может работать заметно быстрее или медленнее, чем отдельный фрагмент, который вы тестируете на скорость.

Частичное решение, которое может уменьшить несоответствие, - это использование -Ofastдля тестирования скорости с добавлением 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;
}

Также благодарю Майка Джарвиса за его таймер.

Обратите внимание (это очень важно), что если вы собираетесь запускать большие фрагменты кода, вам действительно необходимо уменьшить количество итераций, чтобы ваш компьютер не завис.


2
Хороший ответ, кроме отключения оптимизации. Бенчмаркинг -O0кода является большой тратой времени , так как накладные расходы , -O0 а не нормальным -O2или -O3 -march=nativeизменяется дико в зависимости от кода и нагрузки. например, дополнительные именованные переменные tmp требуют времени -O0. Есть и другие способы избежать оптимизации, например, скрытие чего-либо от оптимизатора с помощью volatileне встроенных функций или пустых встроенных операторов asm. -O0даже близко не к пригодному для использования, потому что у кода есть другие узкие места -O0, не такие же, а хуже.
Питер Кордес

1
Ух, -Ogвсе равно не очень реально, в зависимости от кода. По крайней мере -O2, желательно -O3более реалистично. Используйте asm volatile("" ::: "+r"(var))или что-то еще, чтобы компилятор материализовал значение в регистре и предотвратил распространение констант через него.
Питер Кордес

@PeterCordes Еще раз спасибо за понимание. Я обновил содержание -O3и фрагмент кода с помощью asm volatile("" ::: "+r"(var)).
Джек Гиффин

1
asm volatile("" ::: "+r"( i ));кажется ненужным. В оптимизированном коде нет причин заставлять компилятор материализоваться iтак же хорошо, как i<<7внутри цикла. Вы останавливаете его оптимизацию, tmp -= 128вместо того, чтобы каждый раз переключаться. Однако использование результата вызова функции хорошо, если это не так void. Нравится int result = (*function_to_do)( i << 7 );. Вы можете использовать asmзаявление об этом результате.
Питер Кордес

@PeterCordes Еще раз большое спасибо за ваши идеи. Мой пост теперь содержит исправления для возвращаемого значения from, function_to_doчтобы его function_to_doможно было встроить без удаления. Пожалуйста, дайте мне знать, если у вас есть дополнительные предложения.
Джек Гиффин

1

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

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

1

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

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

0

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


Он полагается на clock()функцию из стандартного заголовка C ++.
Петтер

0

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

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

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


0

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

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

0

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

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

0
#include <omp.h>

double start = omp_get_wtime();

// code 

double finish = omp_get_wtime();

double total_time = finish - start;

2
Хотя этот код может решить вопрос, в том числе объяснение того, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщения и, вероятно, приведет к большему количеству голосов за. Помните, что вы отвечаете на вопрос для будущих читателей, а не только для человека, который задает его сейчас. Пожалуйста , измените свой ответ , чтобы добавить объяснения и дать указание о том , что применять ограничения и допущения.
Дхарман
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.