Используя только ANSI C, можно ли измерить время с точностью до миллисекунды или более? Я просматривал time.h, но я нашел только вторую функцию точности.Как измерить время в миллисекундах, используя ANSI C?
ответ
Нет функции ANSI C, которая обеспечивает лучшее временное разрешение более 1 секунды, но функция POSIX gettimeofday
обеспечивает разрешение в микросекундах. Функция часов измеряет только время, затраченное на выполнение процесса, и не является точным во многих системах.
Вы можете использовать эту функцию, как это:
struct timeval tval_before, tval_after, tval_result;
gettimeofday(&tval_before, NULL);
// Some code you want to time, for example:
sleep(1);
gettimeofday(&tval_after, NULL);
timersub(&tval_after, &tval_before, &tval_result);
printf("Time elapsed: %ld.%06ld\n", (long int)tval_result.tv_sec, (long int)tval_result.tv_usec);
Это возвращает Time elapsed: 1.000870
на моей машине.
Небольшое предостережение: gettimeofday() не монотонно, то есть он может прыгать (и даже идти назад), если, например, ваша машина пытается синхронизировать с сетевым сервером времени или другим источником времени. – Dipstick 2009-08-09 09:30:01
Чтобы быть точным: в ISO C99 (который, как я думаю, совместим в этой части с ANSI C), даже не гарантируется * любое * временное разрешение. (ISO C99, 7.23.1p4) – 2010-11-26 23:28:31
Пример небольшого использования был бы таким приятным ... – 2012-04-01 18:43:00
Наилучшая точность, которую вы можете получить, заключается в использовании инструкции «rdtsc», основанной на x86, которая может обеспечивать разрешение на уровне часов (ne обязательно должен учитывать стоимость самого вызова rdtsc, который может легко измеряться при запуске приложения).
Главный улов здесь измеряет количество часов в секунду, которое не должно быть слишком жестким.
#include <time.h>
clock_t uptime = clock()/(CLOCKS_PER_SEC/1000);
Я всегда использую функцию clock_gettime(), возвращая время с часов CLOCK_MONOTONIC. Возвращаемое время - это количество времени, в секундах и наносекундах, с некоторой неопределенной точки в прошлом, например, запуск системы эпохи.
#include <stdio.h>
#include <stdint.h>
#include <time.h>
int64_t timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p)
{
return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
}
int main(int argc, char **argv)
{
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
// Some code I am interested in measuring
clock_gettime(CLOCK_MONOTONIC, &end);
uint64_t timeElapsed = timespecDiff(&end, &start);
}
Под окнами:
SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);
timespec_get
от С11 возвращается до наносекунд, округляется до разрешения реализации.
Похоже, что ANSI ripoff от POSIX 'clock_gettime
.
Пример: printf
выполняется каждые 100 мс на Ubuntu 15.10:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static long get_nanos(void) {
struct timespec ts;
timespec_get(&ts, TIME_UTC);
return (long)ts.tv_sec * 1000000000L + ts.tv_nsec;
}
int main(void) {
long nanos;
long last_nanos;
long start;
nanos = get_nanos();
last_nanos = nanos;
start = nanos;
while (1) {
nanos = get_nanos();
if (nanos - last_nanos > 100000000L) {
printf("current nanos: %ld\n", nanos - start);
last_nanos = nanos;
}
}
return EXIT_SUCCESS;
}
C11 N1570 standard draft7.27.2.5 timespec_get функция говорит:
Если основание TIME_UTC, то tv_sec член установленное на количество секунд с момента реализации реализации , усеченной до целого значения, и член tv_nsec равен , установленному на целое число наносекунд, округленное к разрешению системных часов. (321)
321) Хотя объект struct timespec описывает времена с разрешением наносекунды, доступное разрешение зависит от системы и может даже превышать 1 секунду.
C++ 11 также получил std::chrono::high_resolution_clock
: C++ Cross-Platform High-Resolution Timer
Glibc 2.21 реализует его под sysdeps/posix/timespec_get.c
как:
int
timespec_get (struct timespec *ts, int base)
{
switch (base)
{
case TIME_UTC:
if (__clock_gettime (CLOCK_REALTIME, ts) < 0)
return 0;
break;
default:
return 0;
}
return base;
}
так ясно:
только
TIME_UTC
в настоящее время поддерживаетсяон направляет
__clock_gettime (CLOCK_REALTIME, ts)
, которая является POSIX API: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.htmlLinux x86-64 имеет систему
clock_gettime
вызов.Обратите внимание, что это не безотказный метод микро-бенчмаркинг, потому что:
man clock_gettime
говорит, что эта мера может иметь разрывы, если изменить какое-то время системы настройки в то время как программа работает. Конечно, это должно быть редкое событие, и вы можете его игнорировать.это измеряет время стены, поэтому, если планировщик решает забыть о вашей задаче, он будет работать дольше.
По этим причинам
getrusage()
может быть лучше, лучше инструмент POSIX бенчмаркинг, несмотря на это ниже микросекунд максимальная точность.Дополнительная информация: Measure time in Linux - time vs clock vs getrusage vs clock_gettime vs gettimeofday vs timespec_get?
Реализация портативное решение
Как уже упоминалось здесь, что нет надлежащего решения ANSI с достаточной степенью точности для задачи измерения времени, я хочу писать о способах получения портативного и, по возможности, решения для измерения времени с высоким разрешением.
Monotonic часы против штампов времени
Вообще есть два способа измерения времени:
- монотонной часы;
- текущая (дата) отметка времени.
Первый использует монотонный счетчик часов (иногда его называют клещом счетчиком), который подсчитывает клещ с предопределенной частотой, так что если у вас есть тики значения и частота известна, вы можете легко конвертировать тики пройденное время. На самом деле не гарантируется, что монотонные часы каким-либо образом отражают текущее системное время, он также может подсчитывать тики с момента запуска системы. Но это гарантирует, что часы всегда запускаются все чаще, независимо от состояния системы. Обычно частота привязана к аппаратным источникам с высоким разрешением, поэтому она обеспечивает высокую точность (в зависимости от аппаратного обеспечения, но большинство современных аппаратных средств не имеют проблем с источниками синхронизации с высоким разрешением).
Второй способ обеспечивает (time) значение времени, основанное на текущем значении системного тактового сигнала.Он также может иметь высокое разрешение, но у него есть один главный недостаток: на такое значение времени могут влиять различные системные настройки времени, такие как изменение часового пояса, переход на летнее время (DST), обновление сервера NTP, спящий режим системы и т. Д. на. В некоторых случаях вы можете получить отрицательное истекшее значение времени, которое может привести к неопределенному поведению. На самом деле такой источник времени менее надежный, чем первый.
Итак, первое правило в измерении временных интервалов - использовать монотонные часы, если это возможно. Он обычно имеет высокую точность, и он надежен по дизайну.
Запасного стратегия
При реализации портативным решения, за которое стоит рассмотреть стратегию резервной: использовать монотонные часы, если доступны и возврат к Отметкам времени подходу, если нет монотонной часы в системе.
Окна
Существует большая статья под названием Acquiring high-resolution time stamps на MSDN об измерении времени на Windows, которая описывает все детали, которые могут понадобиться знать о программного и аппаратного обеспечения. Для того, чтобы получить штамп времени с высокой точностью на Windows, вам необходимо:
запрос частоты таймера (тиков в секунду) с QueryPerformanceFrequency:
LARGE_INTEGER tcounter; LARGE_INTEGER freq; if (QueryPerformanceFrequency (&tcounter) != 0) freq = tcounter.QuadPart;
Частота таймер устанавливается на загрузке системы, так что вам нужно чтобы получить его только один раз.
запрос текущее тики значение с QueryPerformanceCounter:
LARGE_INTEGER tcounter; LARGE_INTEGER tick_value; if (QueryPerformanceCounter (&tcounter) != 0) tick_value = tcounter.QuadPart;
масштаба клещи к затраченному времени, то есть до микросекунд:
LARGE_INTEGER usecs = (tick_value - prev_tick_value)/(freq/1000000);
Согласно Microsoft, вы не должны иметь какой-либо проблемы с этим подходом в Windows XP и более поздних версиях в большинстве случаев. Но вы также можете использовать два резервных решения на Windows:
- GetTickCount обеспечивает количество миллисекунд, прошедших с момента запуска системы. Он обертывается каждые 49,7 дней, поэтому будьте осторожны при измерении более длинных интервалов.
- GetTickCount64 - это 64-разрядная версия
GetTickCount
, но она доступна, начиная с Windows Vista и выше.
OS X (MacOS)
OS X (MacOS) имеет свою собственную абсолютную единицы времени Маха, которые представляют собой монотонную часы. Лучший способ начать - статья Apple Technical Q&A QA1398: Mach Absolute Time Units, которая описывает (с примерами кода), как использовать специфичный для Маха API API для получения монотонных тиков. Существует также локальный вопрос о нем, который называется clock_gettime alternative in Mac OS X, который в конце может немного вас смутить, что делать с возможным переполнением значения, поскольку частота счетчика используется в виде числителя и знаменателя.Итак, краткий пример того, как получить истекшее время:
получить тактовую частоту числитель и знаменатель:
#include <mach/mach_time.h> #include <stdint.h> static uint64_t freq_num = 0; static uint64_t freq_denom = 0; void init_clock_frequency() { mach_timebase_info_data_t tb; if (mach_timebase_info (&tb) == KERN_SUCCESS && tb.denom != 0) { freq_num = (uint64_t) tb.numer; freq_denom = (uint64_t) tb.denom; } }
Вы должны сделать это только один раз.
запроса текущее значение клеща с
mach_absolute_time
:uint64_t tick_value = mach_absolute_time();
масштаба клещи к истекшего времени, т.е. микросекундах, используя ранее опрашивается числитель и знаменатель:
uint64_t value_diff = tick_value - prev_tick_value; /* To prevent overflow */ value_diff /= 1000; value_diff *= freq_num; value_diff /= freq_denom;
Основная идея предотвратите переполнение, чтобы уменьшить галочки до желаемой точности перед использованием числителя и знаменателя. Поскольку начальное разрешение таймера в наносекундах, мы делим его на
1000
, чтобы получить микросекунды. Вы можете найти тот же подход, который используется в Chromium's time_mac.c. Если вам действительно нужна точность наносекунды, считайте, что вы читаете How can I use mach_absolute_time without overflowing?.
Linux и UNIX
clock_gettime
вызов является лучшим способом на любой POSIX-дружественной системы. Он может запрашивать время из разных источников синхронизации, а тот, который нам нужен, - CLOCK_MONOTONIC
. Не все системы, которые имеют clock_gettime
поддержку CLOCK_MONOTONIC
, поэтому первое, что вам нужно сделать, это проверить ее наличие:
- если
_POSIX_MONOTONIC_CLOCK
определяется значением>= 0
это означает, чтоCLOCK_MONOTONIC
доклада доступен; если
_POSIX_MONOTONIC_CLOCK
определяется0
это означает, что вы должны дополнительно проверить, работает ли он во время выполнения, я предлагаю использоватьsysconf
:#include <unistd.h> #ifdef _SC_MONOTONIC_CLOCK if (sysconf (_SC_MONOTONIC_CLOCK) > 0) { /* A monotonic clock presents */ } #endif
- иначе монотонные часы не поддерживаются, и вы должны использовать стратегию резервной (Смотри ниже).
Использования clock_gettime
довольно прямо вперед:
получить значение времени:
#include <time.h> #include <sys/time.h> #include <stdint.h> uint64_t get_posix_clock_time() { struct timespec ts; if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0) return (uint64_t) (ts.tv_sec * 1000000 + ts.tv_nsec/1000); else return 0; }
Я уменьшенное время до микросекунд здесь.
высчитывает разницу с предыдущим значением времени получил тот же путь:
uint64_t prev_time_value, time_value; uint64_t time_diff; /* Initial time */ prev_time_value = get_posix_clock_time(); /* Do some work here */ /* Final time */ time_value = get_posix_clock_time(); /* Time difference */ time_diff = time_value - prev_time_value;
Лучшая стратегия запасной вариант заключается в использовании gettimeofday
вызов: это не монотонная, но она обеспечивает довольно хорошее разрешение.Идея такой же, как и с clock_gettime
, но, чтобы получить значение времени, вы должны:
#include <time.h>
#include <sys/time.h>
#include <stdint.h>
uint64_t get_gtod_clock_time()
{
struct timeval tv;
if (gettimeofday (&tv, NULL) == 0)
return (uint64_t) (tv.tv_sec * 1000000 + tv.tv_usec);
else
return 0;
}
Опять же, значение времени уменьшенное до микросекунд.
SGI IRIX
IRIX имеет clock_gettime
вызов, но ему не хватает CLOCK_MONOTONIC
. Вместо этого он имеет свой собственный источник монотонных часов, определенный как CLOCK_SGI_CYCLE
, который вы должны использовать вместо CLOCK_MONOTONIC
с clock_gettime
.
Solaris и HP-UX
Solaris имеет свой собственный интерфейс таймер высокого разрешения gethrtime
, который возвращает текущее значение таймера в наносекунд. Хотя более новые версии Solaris могут иметь clock_gettime
, вы можете придерживаться gethrtime
, если вам нужно поддерживать старые версии Solaris.
Способ применения прост:
#include <sys/time.h>
void time_measure_example()
{
hrtime_t prev_time_value, time_value;
hrtime_t time_diff;
/* Initial time */
prev_time_value = gethrtime();
/* Do some work here */
/* Final time */
time_value = gethrtime();
/* Time difference */
time_diff = time_value - prev_time_value;
}
HP-UX не хватает clock_gettime
, но он поддерживает gethrtime
, которые вы должны использовать точно так же, как и на Solaris.
BeOS
BeOS также имеет свой собственный интерфейс таймер высокого разрешения system_time
который возвращает число микросекунд прошло с тех пор компьютер был загружен.
Пример использования:
#include <kernel/OS.h>
void time_measure_example()
{
bigtime_t prev_time_value, time_value;
bigtime_t time_diff;
/* Initial time */
prev_time_value = system_time();
/* Do some work here */
/* Final time */
time_value = system_time();
/* Time difference */
time_diff = time_value - prev_time_value;
}
OS/2
OS/2 имеет свой собственный API для получения высокоточных временных меток:
запроса частоту таймера (тиков в блок) с
DosTmrQueryFreq
(для компилятора GCC):#define INCL_DOSPROFILE #define INCL_DOSERRORS #include <os2.h> #include <stdint.h> ULONG freq; DosTmrQueryFreq (&freq);
запрос текущих тиков значение с
DosTmrQueryTime
:QWORD tcounter; unit64_t time_low; unit64_t time_high; unit64_t timestamp; if (DosTmrQueryTime (&tcounter) == NO_ERROR) { time_low = (unit64_t) tcounter.ulLo; time_high = (unit64_t) tcounter.ulHi; timestamp = (time_high << 32) | time_low; }
масштаба клещи к истекшему времени, т.е. микросекунд:
uint64_t usecs = (prev_timestamp - timestamp)/(freq/1000000);
Пример реализация
Вы можете ознакомиться с библиотекой plibsys, которая реализует все описанные выше стратегии (подробнее см. Ptimeprofiler * .c).
Обратите внимание на разницу между точностью и точностью.Вы можете получить время с точностью до миллисекунды *, взяв время в секундах и умножить на 1000, но это бесполезно. ms точность не всегда имеет точность ms - хотя они, как правило, лучше, чем точность 1 с. – 2008-12-13 02:22:53
Простой ответ NO, ANSI C не поддерживает миллисекундную точность или лучше. Более сложный ответ зависит от того, что вы пытаетесь сделать - честно говоря, вся область - это кошмар, даже если вы разрешаете использовать широко доступные функции Posix. Вы используете термин «меру», поэтому я предполагаю, что вас интересует интервал, а не «настенные часы». Но пытаетесь ли вы измерить абсолютный период времени или использование процессора вашим процессом? – Dipstick 2009-08-09 09:22:39
Просто хотел сказать, что SOF только что спас мой бекон, опять ;-) – corlettk 2011-05-16 07:31:22