Функция таймера для отображения времени в наносекундах с использованием 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;
  }

Приведенный выше код показывает время в секундах. Как добиться того же за наносекунды и с большей точностью?

Gagneet
источник
приведенный выше код рассчитывается за секунды, я хочу получить ответ за наносекунды ...
gagneet
Чтобы получить хороший ответ, нужно добавить платформу к вопросу (а также, желательно, к заголовку).
Патрик Джонмейер
Помимо получения времени, нужно искать проблемы с микробенчмаркингом (что чрезвычайно сложно) - просто выполнение одного выполнения и получение времени в начале и в конце вряд ли даст достаточную точность.
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 также могут вызывать проблемы . См. Второй пост Себбби, где он заявляет:

QueryPerformanceCounter () и QueryPerformanceFrequency () предлагают немного лучшее разрешение, но имеют другие проблемы. Например, в Windows XP все двухъядерные процессоры AMD Athlon X2 возвращают ПК любого из ядер «случайным образом» (ПК иногда немного перескакивает назад), если вы специально не установите пакет двухъядерных драйверов AMD для устранения проблемы. Мы не заметили никаких других двухъядерных процессоров с аналогичными проблемами (p4 dual, p4 ht, core2 dual, core2 quad, phenom quad).

РЕДАКТИРОВАТЬ 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>. Кроме того, я продемонстрирую, как вы можете шаблонизировать тестовый код на часах, чтобы вы могли быстро переключаться между 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устройство, которое я мог бы использовать.

В качестве другого примера я хочу распечатать среднее количество тактовых циклов на итерацию как число с плавающей запятой, поэтому я создаю другую длительность, основанную на double, которая имеет те же единицы, что и тик часов (вызывается 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
Как на это повлияет троттлинг процессора? Разве тактовая частота не меняется в зависимости от загрузки процессора?
Tejas Kale
@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 ().

VonC
источник
спасибо за информацию, это полезно. я не думал о циклах процессора, чтобы вычислить время, я думаю, что это очень хороший момент, чтобы иметь в виду :-)
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;
}
Gagneet
источник
2
Я проголосовал против, потому что, пытаясь применить этот код, мне пришлось сначала погуглить, почему спецификация времени не определена. Затем мне пришлось погуглить, что такое 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

Gongzhitaao
источник
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;
}

с Digital Mars C ++:

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

который считывает высокопроизводительный таймер на чипе. Я использую это при профилировании.

Уолтер Брайт
источник
2
это полезно, я проверю, является ли процессор x86, так как я использую Apple Mac для экспериментов ... спасибо :-)
gagneet
1
Какие значения пользователь должен дать высоким и низким? Почему вы определяете макрос внутри тела функции? Кроме того, ulonglong, предположительно typedef'd на unsigned long long, не является стандартным типом. Я бы хотел использовать это, но не уверен, как это сделать;)
Джозеф Гарвин
1
unsigned long не подходит для использования под Linux. Вместо этого вы можете рассмотреть возможность использования int, поскольку long и long long являются 64-битными в 64-битном Linux.
Marius
3
Счетчик TSC в настоящее время часто ненадежен: он меняет свою скорость на многих процессорах при изменении частоты и непоследователен для разных ядер, поэтому TSC не всегда растет.
Blaisorblade
1
@Marius: Я реализовал ваш комментарий, используя его unsigned intкак внутренний тип.
Blaisorblade
3

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

Если вы используете Boost, вы можете проверить boost :: posix_time .

Раймон Мартино
источник
хотите, чтобы код был переносимым, увидит библиотеку boost и проверит, могу ли я связать ее с кодом. спасибо :-)
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

Вы можете использовать Embedded 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 () пока подойдет. попробую использовать то же самое для моих целей ...
gagnet
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é