Функція таймера, щоб забезпечити час у наносекундах за допомогою C ++


101

Я хочу обчислити час, який потрібно API, щоб повернути значення. Час, необхідний для такої дії, знаходиться в просторі наносекунд. Оскільки API - це клас / функція C ++, я використовую timer.h для того, щоб зробити те ж саме:

  #include <ctime>
  #include <cstdio>

  using namespace std;

  int main(int argc, char** argv) {

      clock_t start;
      double diff;
      start = clock();
      diff = ( std::clock() - start ) / (double)CLOCKS_PER_SEC;
      cout<<"printf: "<< diff <<'\n';

      return 0;
  }

Вищевказаний код дає час у секундах. Як отримати те саме за наносекунд і з більшою точністю?


вищевказаний код обчислюється в секундах, я хочу отримати відповідь за
наносекунд

Потрібно додати платформу до питання (а краще також і до заголовка), щоб отримати хорошу відповідь.
Патрік Джонмайєр

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

@Blaisorblade: Тим більше, що я виявив, що в деяких моїх тестах clock()це не так швидко, як я вважав, що це було.
Mooing Duck

Відповіді:


83

Те, що інші публікували про повторне виконання функції в циклі, є правильним.

Для Linux (і BSD) ви хочете використовувати clock_gettime () .

#include <sys/time.h>

int main()
{
   timespec ts;
   // clock_gettime(CLOCK_MONOTONIC, &ts); // Works on FreeBSD
   clock_gettime(CLOCK_REALTIME, &ts); // Works on Linux
}

Для вікон ви хочете використовувати QueryPerformanceCounter . І ось докладніше про QPC

Мабуть, є відома проблема з QPC на деяких чіпсетах, тому ви можете переконатися, що у вас немає цих чіпсетів. Крім того, деякі двоядерні AMD також можуть спричинити проблеми . Дивіться друге повідомлення від sebbbi, де він констатує:

QueryPerformanceCounter () та QueryPerformanceFrequency () пропонують трохи краще рішення, але мають різні проблеми. Наприклад, в Windows XP, всі двоядерні процесори AMD Athlon X2 повертають ПК будь-якого з ядер "випадковим чином" (ПК іноді трохи відскакує назад), якщо ви спеціально не встановлюєте двоядерний пакет драйверів AMD для усунення проблеми. Ми не помітили жодних інших процесорів dual + core з подібними проблемами (p4 dual, p4 ht, core2 dual, core2 quad, fenom quad).

EDIT 2013/07/16:

Схоже, існує певна суперечка щодо ефективності QPC за певних обставин, як зазначено в http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx

... Хоча QueryPerformanceCounter і QueryPerformanceFrequency зазвичай регулюються для декількох процесорів, помилки в BIOS або драйвери можуть призвести до того, що ці підпрограми повертають різні значення при переміщенні потоку від одного процесора до іншого ...

Однак у цій відповіді StackOverflow https://stackoverflow.com/a/4588605/34329 зазначено, що QPC повинен добре працювати в будь-якій ОС MS після пакета оновлення 2 пакета оновлення Win XP.

Ця стаття показує, що Windows 7 може визначити, чи мають процесор (-и) інваріантний TSC і повертається до зовнішнього таймера, якщо вони не мають. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html Синхронізація між процесорами все ще залишається проблемою.

Інші точні читання, пов'язані з таймерами:

Детальнішу інформацію див. У коментарях.


1
Я бачив, як годинник TSC перекошений на старшому подвійному ПК Xeon, але не так вже й погано, як на Athlon X2 з увімкненою синхронною синхронізацією на C1. При синхронізації синхросигналу C1, виконання інструкції HLT сповільнює тактову частоту, внаслідок чого TSC на холостих ядрах збільшується повільніше, ніж на активних ядрах.
bk1e

6
CLOCK_MONOTONIC працює на доступних версіях Linux.
Бернард

1
@Bernard - Це потрібно недавно додати з моменту останнього перегляду цього. Дякую за голову вгору
скорбота

3
Насправді вам доведеться використовувати CLOCK_MONOTONIC_RAW, якщо вона є, для того, щоб апаратний час не коригувався NTP.

Як обговорювалося тут, правильне використання QPC не використовувати лічильник TSC, принаймні там , де вона , як відомо, ненадійні: stackoverflow.com/q/510462/53974
Blaisorblade

69

Ця нова відповідь використовує засоби C ++ 11 <chrono>. Хоча існують і інші відповіді , які показують , як використовувати <chrono>, жоден з них не показує , як використовувати <chrono>з RDTSCоб'єкта , зазначеного в декількох інших відповідей тут. Так я думав , що показати , як використовувати RDTSCз <chrono>. Крім того , я покажу вам , як ви можете templatize коду тестування на годиннику , так що ви можете швидко перемикатися між , RDTSCі ваша система вбудована в годиннику об'єктів (які, ймовірно , буде заснована на clock(), clock_gettime()і / або QueryPerformanceCounter.

Зауважте, що RDTSCінструкція є специфічною для x86. QueryPerformanceCounterє лише Windows. І clock_gettime()це лише POSIX. Нижче я ввожу два нові годинники: std::chrono::high_resolution_clockі std::chrono::system_clock, які, якщо ви можете припустити C ++ 11, тепер є кросплатформенними.

По-перше, ось як ви створюєте годинник, сумісний із C ++ 11, з rdtscінструкції щодо складання Intel . Я назву це x::clock:

#include <chrono>

namespace x
{

struct clock
{
    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2'800'000'000>       period; // My machine is 2.8 GHz
    typedef std::chrono::duration<rep, period> duration;
    typedef std::chrono::time_point<clock>     time_point;
    static const bool is_steady =              true;

    static time_point now() noexcept
    {
        unsigned lo, hi;
        asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
        return time_point(duration(static_cast<rep>(hi) << 32 | lo));
    }
};

}  // x

Все, що робить цей годинник, - це підрахунок циклів процесора та збереження їх у непідписане 64-бітове ціле число. Вам може знадобитися налаштувати синтаксис мови збірки для вашого компілятора. Або ваш компілятор може запропонувати невід'ємний текст, який ви можете використовувати замість цього (наприклад now() {return __rdtsc();}).

Щоб побудувати годинник, ви повинні надати йому подання (тип зберігання). Ви також повинні надати часовий період, який повинен бути константами часу компіляції, навіть якщо ваша машина може змінювати тактову частоту в різних режимах живлення. І з тих, що ви можете легко визначити "рідну" тривалість часу та часовий термін з точки зору цих основ.

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

Нижче наведено приклад коду, який показує, як користуватися x::clock. Насправді я шаблонував код на годиннику, як хотів би показати, як можна використовувати безліч різних годин із точно таким же синтаксисом. Цей конкретний тест показує, що таке циклічні накладні витрати під час запуску того, що ви хочете провести під циклом:

#include <iostream>

template <class clock>
void
test_empty_loop()
{
    // Define real time units
    typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
    // or:
    // typedef std::chrono::nanoseconds nanoseconds;
    // Define double-based unit of clock tick
    typedef std::chrono::duration<double, typename clock::period> Cycle;
    using std::chrono::duration_cast;
    const int N = 100000000;
    // Do it
    auto t0 = clock::now();
    for (int j = 0; j < N; ++j)
        asm volatile("");
    auto t1 = clock::now();
    // Get the clock ticks per iteration
    auto ticks_per_iter = Cycle(t1-t0)/N;
    std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
    // Convert to real time units
    std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
              << "ps per iteration\n";
}

Перше, що робить цей код - створити одиницю "в реальному часі" для відображення результатів. Я вибрав пікосекунди, але ви можете вибрати будь-які вподобані вам одиниці, як цілі, так і плаваючі точки. Як приклад є попередньо виготовлений std::chrono::nanosecondsблок, який я міг би використати.

В якості іншого прикладу я хочу роздрукувати середню кількість тактових циклів за ітерацію як плаваючу крапку, тому я створюю іншу тривалість, засновану на подвійному, який має ті самі одиниці, що і галочка годинника (називається Cycleв коді).

Цикл приурочений до дзвінків clock::now()з обох сторін. Якщо ви хочете назвати тип, який повертається з цієї функції, це:

typename clock::time_point t0 = clock::now();

(як чітко показано в x::clockприкладі, і це стосується також системних годин).

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

Ви можете отримати підрахунок за будь-яку тривалість за допомогою функції count()члена. Це повертає внутрішнє представлення. Нарешті, я використовую std::chrono::duration_castдля перетворення тривалості Cycleу тривалість picosecondsта роздруковую її.

Користуватися цим кодом просто:

int main()
{
    std::cout << "\nUsing rdtsc:\n";
    test_empty_loop<x::clock>();

    std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
    test_empty_loop<std::chrono::high_resolution_clock>();

    std::cout << "\nUsing std::chrono::system_clock:\n";
    test_empty_loop<std::chrono::system_clock>();
}

Вище я здійснюю тест, використовуючи наші домашні вироби x::clock, і порівнюю ці результати з використанням двох годинників, що постачаються до системи: std::chrono::high_resolution_clockі std::chrono::system_clock. Для мене це виводить:

Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration

Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration

Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration

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

Зверніть увагу, як мій код повністю не містить "магічних констант конверсії". Дійсно, у всьому прикладі є лише два магічні числа:

  1. Тактова частота моєї машини для визначення x::clock.
  2. Кількість повторень для перевірки. Якщо зміна цього числа призводить до того, що ваші результати сильно різняться, вам, мабуть, слід збільшити кількість повторень або спорожнити комп'ютер конкуруючих процесів під час тестування.

5
"RDTSC - це лише Intel", ви справді маєте на увазі архітектуру та похідні x86, чи не так? Інструкції мають мікросхеми AMD, Cyrix, Transmeta x86 , а процесори Intel RISC і ARM цього не мають.
Бен Войгт

1
@BenVoigt: +1 Так, ваше виправлення цілком правильне, дякую.
Говард Хіннант

1
Як вплине на це процесорування? Чи не змінюється тактова частота на основі завантаження процесора?
Техас Кале

@TejasKale: Це описано у відповіді у двох параграфах поспіль, починаючи з "Щоб побудувати годинник ти ...". Зазвичай код часу не вимірює роботу, яка блокує потік (але може). І так, як правило, ваш процесор не заглушить. Але якщо ви вимірюєте код, пов’язаний зі сном, блокуванням мютексу, очікуванням_змінного очікування тощо, rdtscгодинник, ймовірно, має неточні перетворення на інші одиниці. Це гарна ідея встановити свої виміри, щоб ви могли легко змінювати та порівнювати годинник (як показано у цій відповіді).
Говард Хінант

28

З таким рівнем точності було б краще міркувати в галочці процесора, а не в системному дзвінку, як clock () . І не забувайте, що якщо на виконання настанови потрібно більше однієї наносекунди ... мати точність наносекунди майже неможливо.

Все-таки щось подібне - це початок:

Ось фактичний код для вибору кількості 80x86 тактових годин процесора, минулих з моменту останнього запуску процесора. Він буде працювати над Pentium і вище (386/486 не підтримується). Цей код насправді специфічний для MS Visual C ++, але його, ймовірно, можна легко перенести на все інше, якщо він підтримує вбудовану збірку.

inline __int64 GetCpuClocks()
{

    // Counter
    struct { int32 low, high; } counter;

    // Use RDTSC instruction to get clocks count
    __asm push EAX
    __asm push EDX
    __asm __emit 0fh __asm __emit 031h // RDTSC
    __asm mov counter.low, EAX
    __asm mov counter.high, EDX
    __asm pop EDX
    __asm pop EAX

    // Return result
    return *(__int64 *)(&counter);

}

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

Використання цифр часу :
Якщо вам потрібно перевести відліки годин у справжній минулий час, розділіть результати на тактову частоту вашого чіпа. Пам'ятайте, що "номінальний" ГГц, ймовірно, трохи відрізнятиметься від фактичної швидкості вашого чіпа. Щоб перевірити справжню швидкість вашого чіпа, ви можете скористатися кількома дуже хорошими утилітами або викликом Win32, QueryPerformanceFrequency ().


дякую за інформацію, це корисно. я не думав про цикли процесора, щоб обчислити час, я думаю, що це дуже хороший момент, який слід пам’ятати :-)
gagneet

4
Використання QueryPerformanceFrequency () для перетворення рахунків TSC у минулий час може не працювати. QueryPerformanceCounter () використовує HPET (високоточний таймер подій) у Vista, коли він доступний. Він використовує таймер управління потужністю ACPI, якщо користувач додає / USEPMTIMER до boot.ini.
bk1e

23

Щоб правильно це зробити, ви можете скористатись одним із двох способів - або йти, RDTSCабо з clock_gettime(). Другий приблизно в 2 рази швидший і має перевагу в наданні потрібного абсолютного часу. Зауважте, що для RDTSCкоректної роботи вам потрібно використовувати його як зазначено (інші коментарі на цій сторінці мають помилки, і можуть давати неправильні значення часу на певних процесорах)

inline uint64_t rdtsc()
{
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx" );
    return (uint64_t)hi << 32 | lo;
}

та для clock_gettime: (Я вибрав роздільну здатність мікросекунди довільно)

#include <time.h>
#include <sys/timeb.h>
// needs -lrt (real-time lib)
// 1970-01-01 epoch UTC time, 1 mcs resolution (divide by 1M to get time_t)
uint64_t ClockGetTime()
{
    timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec / 1000LL;
}

терміни та отримані значення:

Absolute values:
rdtsc           = 4571567254267600
clock_gettime   = 1278605535506855

Processing time: (10000000 runs)
rdtsc           = 2292547353
clock_gettime   = 1031119636

22

Я використовую наступні для отримання бажаних результатів:

#include <time.h>
#include <iostream>
using namespace std;

int main (int argc, char** argv)
{
    // reset the clock
    timespec tS;
    tS.tv_sec = 0;
    tS.tv_nsec = 0;
    clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    ...
    ... <code to check for the time to be put here>
    ...
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    cout << "Time taken is: " << tS.tv_sec << " " << tS.tv_nsec << endl;

    return 0;
}

2
Я спростував, тому що намагаючись застосувати цей код, мені довелося спочатку google, чому timepec не визначений. Тоді мені довелося google whats POSIX ... і тому, як я зрозумів, цей код не стосується користувачів Windows, які дотримуються стандартної бібліотеки.
Даніель Кац

8

Для 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_;
};

Або для C ++ 03 на * nix,

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


5

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

Наприклад, якщо ви оцінюєте, що ваша функція може зайняти 800 нс, запустіть її в циклі десять мільйонів разів (що займе приблизно 8 секунд). Розділіть загальний час на десять мільйонів, щоб отримати час за дзвінок.


фактично, я намагаюся отримати продуктивність api для певного дзвінка. для кожного прогону він може дати інший час, це може вплинути на графік, який я роблю для покращення продуктивності ... отже, час у наносекундах. але так, це чудова ідея, розглянемо її.
gagneet

5

Ви можете використовувати таку функцію з gcc, що працює під процесорами x86:

unsigned long long rdtsc()
{
  #define rdtsc(low, high) \
         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))

  unsigned int low, high;
  rdtsc(low, high);
  return ((ulonglong)high << 32) | low;
}

з цифровим Марсом C ++:

unsigned long long rdtsc()
{
   _asm
   {
        rdtsc
   }
}

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


2
це корисно, я перевірю, чи процесор x86, так як я використовую яблучний Mac для експериментів ... дякую :-)
gagneet

1
Які значення повинен дати користувач для високих і низьких? Чому ви визначаєте макрос всередині тіла функції? Крім того, недовговічне, імовірно, тип, який не підписався довго, не є стандартним типом. Я хотів би скористатися цим, але не впевнений, як;)
Джозеф Гарвін

1
unsigned long - це не правильна річ, яка використовується під Linux. Ви можете розглянути можливість використання int замість того, що довгі та довгі - обидва 64-розрядні в 64-розрядному Linux.
Маріус

3
Сьогодні лічильник TSC часто є ненадійним: він змінює свою швидкість на багатьох процесорах при зміні частоти і є непослідовним для різних ядер, отже, TSC не завжди зростає.
Blaisorblade

1
@Marius: Я реалізував ваш коментар, використовуючи unsigned intяк внутрішній тип.
Blaisorblade

3

Якщо вам потрібна підсекундна точність, вам потрібно використовувати специфічні для системи розширення, і вам доведеться перевірити документацію для операційної системи. POSIX підтримує до мікросекунд з gettimeofday , але нічого більш точного, оскільки комп'ютери не мали частоти понад 1 ГГц.

Якщо ви використовуєте Boost, ви можете перевірити boost :: posix_time .


хочу зберегти код портативним, побачить збільшити бібліотеку і перевірить, чи можу я поєднати це з кодом. спасибі :-)
gagneet

3

Я використовую код Borland тут - код ti_hund дає мені кілька разів негативну кількість, але терміни досить хороші.

#include <dos.h>

void main() 
{
struct  time t;
int Hour,Min,Sec,Hun;
gettime(&t);
Hour=t.ti_hour;
Min=t.ti_min;
Sec=t.ti_sec;
Hun=t.ti_hund;
printf("Start time is: %2d:%02d:%02d.%02d\n",
   t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);
....
your code to time
...

// read the time here remove Hours and min if the time is in sec

gettime(&t);
printf("\nTid Hour:%d Min:%d Sec:%d  Hundreds:%d\n",t.ti_hour-Hour,
                             t.ti_min-Min,t.ti_sec-Sec,t.ti_hund-Hun);
printf("\n\nAlt Ferdig Press a Key\n\n");
getch();
} // end main

3

Використовуючи метод Брока Адамса, з простим класом:

int get_cpu_ticks()
{
    LARGE_INTEGER ticks;
    QueryPerformanceFrequency(&ticks);
    return ticks.LowPart;
}

__int64 get_cpu_clocks()
{
    struct { int32 low, high; } counter;

    __asm cpuid
    __asm push EDX
    __asm rdtsc
    __asm mov counter.low, EAX
    __asm mov counter.high, EDX
    __asm pop EDX
    __asm pop EAX

    return *(__int64 *)(&counter);
}

class cbench
{
public:
    cbench(const char *desc_in) 
         : desc(strdup(desc_in)), start(get_cpu_clocks()) { }
    ~cbench()
    {
        printf("%s took: %.4f ms\n", desc, (float)(get_cpu_clocks()-start)/get_cpu_ticks());
        if(desc) free(desc);
    }
private:
    char *desc;
    __int64 start;
};

Приклад використання:

int main()
{
    {
        cbench c("test");
        ... code ...
    }
    return 0;
}

Результат:

тест зайняв: 0,0002 мс

Має деякий набір функціональних дзвінків, але він повинен бути більш ніж швидким :)


3

Ви можете використовувати вбудований Profiler (безкоштовний для Windows та Linux), який має інтерфейс до багатоплатформного таймера (у кількості циклів процесора) і може дати вам кількість циклів в секунду:

EProfilerTimer timer;
timer.Start();

... // Your code here

const uint64_t number_of_elapsed_cycles = timer.Stop();
const uint64_t nano_seconds_elapsed =
    mumber_of_elapsed_cycles / (double) timer.GetCyclesPerSecond() * 1000000000;

Перерахунок кількості циклу до часу, можливо, небезпечна операція з сучасними процесорами, де частоту процесора можна динамічно змінювати. Тому, щоб переконатися в правильності перетвореного часу, необхідно виправити частоту процесора перед профілюванням.


2

Якщо це для Linux, я використовував функцію "gettimeofday", яка повертає структуру, яка дає секунди та мікросекунди з епохи. Потім ви можете використовувати timersub, щоб відняти два, щоб отримати різницю у часі, і перетворити його на будь-яку точність часу, яку ви хочете. Однак ви вказуєте наносекунди, і схоже, що функція clock_gettime () - це те, що ви шукаєте. Він вкладає час у перерахунку на секунди та наносекунди в структуру, яку ви переходите до нього.


clock_gettime () наразі має зробити трюк. спробую використовувати те саме для моїх цілей ...
gagneet

2

Що ти думаєш про це:

    int iceu_system_GetTimeNow(long long int *res)
    {
      static struct timespec buffer;
      // 
    #ifdef __CYGWIN__
      if (clock_gettime(CLOCK_REALTIME, &buffer))
        return 1;
    #else
      if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &buffer))
        return 1;
    #endif
      *res=(long long int)buffer.tv_sec * 1000000000LL + (long long int)buffer.tv_nsec;
      return 0;
    }

2

Ось приємний таймер Boost, який добре працює:

//Stopwatch.hpp

#ifndef STOPWATCH_HPP
#define STOPWATCH_HPP

//Boost
#include <boost/chrono.hpp>
//Std
#include <cstdint>

class Stopwatch
{
public:
    Stopwatch();
    virtual         ~Stopwatch();
    void            Restart();
    std::uint64_t   Get_elapsed_ns();
    std::uint64_t   Get_elapsed_us();
    std::uint64_t   Get_elapsed_ms();
    std::uint64_t   Get_elapsed_s();
private:
    boost::chrono::high_resolution_clock::time_point _start_time;
};

#endif // STOPWATCH_HPP


//Stopwatch.cpp

#include "Stopwatch.hpp"

Stopwatch::Stopwatch():
    _start_time(boost::chrono::high_resolution_clock::now()) {}

Stopwatch::~Stopwatch() {}

void Stopwatch::Restart()
{
    _start_time = boost::chrono::high_resolution_clock::now();
}

std::uint64_t Stopwatch::Get_elapsed_ns()
{
    boost::chrono::nanoseconds nano_s = boost::chrono::duration_cast<boost::chrono::nanoseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(nano_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_us()
{
    boost::chrono::microseconds micro_s = boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(micro_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_ms()
{
    boost::chrono::milliseconds milli_s = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(milli_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_s()
{
    boost::chrono::seconds sec = boost::chrono::duration_cast<boost::chrono::seconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(sec.count());
}

2

Мінімалістичне копіювання та вставлення структури + ліниве використання

Якщо ідея полягає у створенні мінімалістичної структури, яку ви можете використовувати для швидких тестів, то я пропоную вам просто скопіювати та вставити будь-яке місце у вашому файлі C ++ одразу після цього #include. Це єдиний екземпляр, в якому я жертвую форматуванням стилю Allman.

Ви можете легко регулювати точність у першому рядку структури. Можливі значення: nanoseconds, microseconds, milliseconds, seconds, minutes, або hours.

#include <chrono>
struct MeasureTime
{
    using precision = std::chrono::microseconds;
    std::vector<std::chrono::steady_clock::time_point> times;
    std::chrono::steady_clock::time_point oneLast;
    void p() {
        std::cout << "Mark " 
                << times.size()/2
                << ": " 
                << std::chrono::duration_cast<precision>(times.back() - oneLast).count() 
                << std::endl;
    }
    void m() {
        oneLast = times.back();
        times.push_back(std::chrono::steady_clock::now());
    }
    void t() {
        m();
        p();
        m();
    }
    MeasureTime() {
        times.push_back(std::chrono::steady_clock::now());
    }
};

Використання

MeasureTime m; // first time is already in memory
doFnc1();
m.t(); // Mark 1: next time, and print difference with previous mark
doFnc2();
m.t(); // Mark 2: next time, and print difference with previous mark
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.t(); // prints 'Mark 3: 123123' etc...

Стандартний вихідний результат

Mark 1: 123
Mark 2: 32
Mark 3: 433234

Якщо ви хочете резюме після виконання

Якщо ви хочете, щоб звіт був згодом, тому що, наприклад, ваш код між ними також записує до стандартного виводу. Потім додайте таку функцію до структури (безпосередньо перед MeasureTime ()):

void s() { // summary
    int i = 0;
    std::chrono::steady_clock::time_point tprev;
    for(auto tcur : times)
    {
        if(i > 0)
        {
            std::cout << "Mark " << i << ": "
                    << std::chrono::duration_cast<precision>(tprev - tcur).count()
                    << std::endl;
        }
        tprev = tcur;
        ++i;
    }
}

Тоді ви можете просто використовувати:

MeasureTime m;
doFnc1();
m.m();
doFnc2();
m.m();
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.m();
m.s();

Який буде перераховувати всі позначки так само, як і раніше, але потім після того, як буде виконаний інший код. Зауважте, що не слід використовувати і те, m.s()і m.t().


Чудово працює з OpenMP на Ubuntu 16.04. Велике спасибі, це найкраще відповість ІМО!
Íhor Mé
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.