Функция таймера для предоставления времени в нано секундах с использованием C++
Я хочу вычислить время, которое требуется 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;
}
Приведенный выше код дает время в секундах. Как получить то же самое за нано секунды и с большей точностью?
16 ответов
То, что другие опубликовали о повторном запуске функции в цикле, является правильным.
Для 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 для решения этой проблемы. Мы не заметили никаких других двухъядерных процессоров, имеющих подобные проблемы (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 или драйверах могут приводить к тому, что эти подпрограммы возвращают разные значения при перемещении потока от одного процессора к другому...
Однако в этом ответе Stackru /questions/28580630/yavlyaetsya-li-systemnanotime-polnostyu-bespoleznyim/28580633#28580633 говорится, что QPC должен нормально работать на любой ОС MS после Win XP с пакетом обновления 2.
В этой статье показано, что Windows 7 может определить, есть ли у процессора (процессоров) инвариантный TSC, и откатится к внешнему таймеру, если их нет. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html Синхронизация между процессорами по-прежнему остается проблемой.
Другое хорошее чтение, связанное с таймерами:
- https://blogs.oracle.com/dholmes/entry/inside_the_hotspot_vm_clocks
- http://lwn.net/Articles/209101/
- http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html
- Статус QueryPerformanceCounter?
Смотрите комментарии для более подробной информации.
Этот новый ответ использует 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-совместимые часы из Intel rdtsc
Инструкция по монтажу. Я позвоню 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
Это показывает, что у каждого из этих часов есть различный период тиков, поскольку такты на итерацию сильно различаются для каждого такта. Однако при преобразовании в известную единицу времени (например, пикосекунды) я получаю примерно одинаковый результат для каждых часов (ваш пробег может отличаться).
Обратите внимание, что мой код полностью свободен от "магических констант преобразования". Действительно, во всем примере есть только два магических числа:
- Тактовая частота моей машины, чтобы определить
x::clock
, - Количество итераций для проверки. Если изменение этого числа приводит к значительным изменениям результатов, то, вероятно, следует увеличить количество итераций или освободить компьютер от конкурирующих процессов во время тестирования.
С таким уровнем точности было бы лучше рассуждать о тике процессора, а не о системных вызовах, таких как 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().
Чтобы сделать это правильно, вы можете использовать один из двух способов, либо пойти с 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
Я использую следующее, чтобы получить желаемые результаты:
#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;
}
Для 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;
}
Вы можете использовать следующую функцию с 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
}
}
который читает высокопроизводительный таймер на чипе. Я использую это при выполнении профилирования.
В общем, для определения времени, которое требуется для вызова функции, вы хотите сделать это намного больше, чем один раз. Если вы вызываете свою функцию только один раз, и ее запуск занимает очень короткое время, у вас все еще есть накладные расходы на фактический вызов функций таймера, и вы не знаете, сколько времени это займет.
Например, если вы оцениваете, что выполнение вашей функции может занять 800 нс, вызовите ее в цикле десять миллионов раз (что затем займет около 8 секунд). Разделите общее время на десять миллионов, чтобы получить время на звонок.
Если вам нужна точность ниже секунды, вам нужно использовать системные расширения, и вам придется сверяться с документацией для операционной системы. POSIX поддерживает до микросекунд с gettimeofday, но ничего более точного, поскольку у компьютеров нет частот выше 1 ГГц.
Если вы используете Boost, вы можете проверить boost:: posix_time.
Вы можете использовать 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;
Пересчет количества циклов во время, возможно, является опасной операцией с современными процессорами, где частота процессора может изменяться динамически. Поэтому, чтобы быть уверенным, что преобразованные времена правильные, необходимо зафиксировать частоту процессора перед профилированием.
Я использую код 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
Используя метод Брока Адамса, с простым классом:
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 мс
Имеет некоторые накладные расходы при вызове функции, но должен быть более чем достаточно быстрым:)
Минималистичное копирование и вставка-структура + ленивое использование
Если идея состоит в том, чтобы иметь минималистичную структуру, которую вы можете использовать для быстрых тестов, то я предлагаю вам просто скопировать и вставить в любом месте вашего файла C++ сразу после #include
"S. Это единственный случай, когда я жертвую форматированием в стиле 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()
,
Что ты об этом думаешь:
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;
}
Вот хороший 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());
}
Если это для Linux, я использую функцию "gettimeofday", которая возвращает структуру, которая дает секунды и микросекунды с начала эпохи. Затем вы можете использовать timersub, чтобы вычесть эти два значения, чтобы получить разницу во времени, и преобразовать ее в любую точность времени, которую вы хотите. Однако вы указываете наносекунды, и похоже, что функция clock_gettime() - это то, что вы ищете. Он помещает время в секундах и наносекундах в структуру, которую вы передаете ему.
plf::nanotimer - легкий вариант для этого, работает в Windows, Linux, Mac и BSD и т.д. Имеет точность ~ микросекунды в зависимости от ОС:
#include "plf_nanotimer.h"
#include <iostream>
int main(int argc, char** argv)
{
plf::nanotimer timer;
timer.start()
// Do something here
double results = timer.get_elapsed_ns();
std::cout << "Timing: " << results << " nanoseconds." << std::endl;
return 0;
}