Як обчислити час виконання фрагмента коду в C ++


121

Я повинен обчислити час виконання фрагмента коду С ++ за секунди. Він повинен працювати або на машинах 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
Майте на увазі, що будь-яке порівняння на основі часу може бути неточним через те, що ОС може не запускати ваш потік від початку до кінця. Це може перервати його та запустити інші потоки, переплетені з вашими, що матиме значний вплив на фактичний час, необхідний для завершення вашої операції. Ви можете запускати кілька разів і оцінювати результати; ви можете мінімізувати кількість інших запущених процесів. Але жодне з них не усуне ефект підвіски нитки повністю.
Мордахай

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
Для подальшої довідки: я просто кидаю його у файл заголовка і використовую його. Радий, що це є.
Daniel Handojo

1
Я вважаю, що метод gettimeofdayможе дати ненавмисний результат, якщо змінити системний годинник. Якщо це стане для вас проблемою, ви можете clock_gettimeзамість цього поглянути .
Азмісов

Чи має цей метод для 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


Я отримую цю помилку з вашим рішенням c ++ 11:/usr/lib/x86_64-linux-gnu/libstdc++.so.6: version GLIBCXX_3.4.19 not found (required by ../cpu_2d/g500)
user9869932

@julianromera, яку платформу ти використовуєш? ви встановили бібліотеку libstdc ++ та g ++?
gongzhitaao

Це сітка Slurm Linux ubuntu 12. Я щойно виправив це. Я додав -static-libstdc ++ в кінці лінкера. Дякую за запитання @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;
}

Коли він 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 застарілий (десь до підвищення 1,50) - auto_cpu_timer може бути більш доречним.
davidA

3
@meowsqueak hmm, схоже, auto_cpu_timer вимагає підключення системної бібліотеки Boost, тому це більше не є рішенням лише для заголовка. Дуже погано ... раптово робить інші варіанти привабливішими.
Томаш Андрле

1
так, це хороший момент, якщо ви ще не зв’язали Boost, то це більше проблем, ніж це варто. Але якщо ви вже це зробите, це працює досить непогано.
davidA

@meowsqueak Так, або для деяких швидких тестів на еталон, просто отримайте цю старішу версію Boost.
Томаш Андрле

@TomasAndrle Посилання більше не існує.
Чжен Ку

5

Windows надає функцію QueryPerformanceCounter (), а Unix має gettimeofday () Обидві функції можуть вимірювати принаймні 1 мікросекунд різниці.


Але використання windows.h обмежене. Одне і те ж скомпільоване джерело має працювати і в Windows, і в Unix. Як впоратися з цією проблемою?
AhmetB - Google

2
Тоді знайдіть бібліотеку обгортки stackoverflow.com/questions/1487695/…
Капітан Комік

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

3

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

(посилання вище - на сторінку французької вікіпедії, але на ній є зразки коду С ++, англійська версія тут )


2

Я пропоную використовувати стандартні функції бібліотеки для отримання інформації про час від системи.

Якщо ви хочете більш чітку роздільну здатність, виконайте більше ітерацій виконання. Замість того, щоб запустити програму один раз і отримати зразки, запустіть її 1000 і більше разів.


2

Краще запустити внутрішній цикл кілька разів із тимчасовим виконанням часу лише один раз і середнім, розділивши повтори внутрішнього циклу, ніж виконати всю справу (цикл + терміни виконання) на кілька разів і середню. Це зменшить накладні витрати часового коду щодо продуктивності та фактичного профілю.

Зверніть свої таймерні виклики для відповідної системи. Для Windows QueryPerformanceCounter досить швидкий і "безпечний" у використанні.

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


2

(специфічне рішення для Windows) Поточним (приблизно у 2017 році) способом отримати точні таймінги під Windows є використання "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 vars коштує часу на . Є й інші способи уникнути оптимізації речей, як-от приховування речей від оптимізатора за допомогою не-вбудованих функцій чи порожніх вбудованих операторів asm. навіть не є близьким до використання, оскільки код має різні вузькі місця , не однакові, але й гірші. -O0volatile-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 Дякую ще раз вам або ваші погляди. Тепер моя публікація містить виправлення повернутого значення, 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 :: таймер , ймовірно, дасть вам стільки точності, скільки вам потрібно. Це ніде не є достатньо точним, щоб сказати вам, як a = a+1;триватиме час, але я з якої причини вам доведеться встигнути щось, що займає пару наносекунд?


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

0

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

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

Ви можете знайти заголовок c ++ 11 тут .


0

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