Как измерить время в миллисекундах с помощью ANSI C?

Используя только ANSI C, есть ли способ измерения времени с точностью до миллисекунд или более? Я просматривал time.h, но я нашел только функции второй точности.

11.12.2008 23:09:50
Обратите внимание на разницу между точностью и точностью. Вы можете получить время с точностью до миллисекунды, считая время в секундах и умножая его на 1000, но это бесполезно. Функции точности с точностью до мс не обязательно имеют точность с точностью до мс - хотя они обычно работают лучше, чем с точностью до 1 с.
Steve Jessop 13.12.2008 02:22:53
Простой ответ - НЕТ, ANSI C не поддерживает точность в миллисекундах или выше. Более сложный ответ зависит от того, что вы пытаетесь сделать - честно говоря, вся область - это кошмар - даже если вы разрешаете использовать широко доступные функции Posix. Вы используете термин «мера», поэтому я предполагаю, что вас интересует интервал, а не «настенные часы». Но вы пытаетесь измерить абсолютный период времени или использование процессора вашим процессом?
Dipstick 9.08.2009 09:22:39
Просто хотел сказать, чтобы SOF просто спас мой бекон, опять же ;-)
corlettk 16.05.2011 07:31:22
8 ОТВЕТОВ
РЕШЕНИЕ

Нет функции 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на моей машине.

91
17.11.2014 19:01:10
Небольшое предостережение: gettimeofday () не является монотонным, то есть может прыгать (и даже идти назад), если, например, ваша машина пытается синхронизироваться с сетевым сервером времени или другим источником времени.
Dipstick 9.08.2009 09:30:01
Чтобы быть точным: В ISO C99 (который я думаю , совместим в этой части с ANSI C) не существует даже гарантия любого временного разрешения. (ISO C99, 7.23.1p4)
Roland Illig 26.11.2010 23:28:31
Стоит отметить, что timeval::tv_usecвсегда меньше одной секунды, это зацикливание. Т.е. для того, чтобы взять разницу во времени больше 1 сек, нужно:long usec_diff = (e.tv_sec - s.tv_sec)*1000000 + (e.tv_usec - s.tv_usec);
Alexander Malakhov 15.10.2012 04:06:37
@Dipstick: но учтите, что, например, NTP никогда не перемещает ваши часы назад, пока вы явно не скажете это сделать.
thejh 22.05.2013 07:01:57
@AlexanderMalakhov логика вычитания времени заключена внутри timersubфункции. Мы можем использовать tval_resultзначения (tv_sec и tv_usec) как есть.
x4444 20.08.2019 04:37:01

Наилучшая точность, которую вы, возможно, можете получить, используя инструкцию «rdtsc» только для x86, которая может обеспечить разрешение на уровне часов (конечно, не нужно учитывать стоимость самого вызова rdtsc, которую можно легко измерить на запуск приложения).

Основным уловом здесь является измерение количества часов в секунду, что не должно быть слишком сложным.

4
12.12.2008 00:15:16
Возможно, вам также придется беспокоиться о сходстве процессоров, поскольку на некоторых компьютерах вы можете отправлять вызовы RDTSC более чем одному процессору, и их счетчики RDTSC могут не синхронизироваться.
Will Dean 19.12.2008 09:13:38
Более того, некоторые процессоры не имеют монотонно увеличивающегося TSC - например, режимы энергосбережения, которые снижают частоту процессора. Использование RDTSC для чего угодно, кроме очень коротких локализованных таймингов, является ОЧЕНЬ плохой идеей.
snemarch 22.02.2013 10:08:56
Кстати, дрейф ядра, упомянутый @WillDean и использующий rdtsc для синхронизации, является причиной того, что ряд игр не работал на (ранних?) Многоядерных процессорах AMD64 - мне пришлось ограничиться одноядерным сродством на моем x2 4400+ для ряд названий.
snemarch 22.02.2013 10:10:42
#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);
46
28.04.2010 19:18:30
CLOCKS_PER_SEC установлен на 1000000 во многих системах. Напечатайте его значение, чтобы быть уверенным, прежде чем использовать его таким образом.
ysap 27.01.2012 21:40:38
Так как это тактовая частота в секунду, не имеет значения, какое это значение, результирующее значение от clock () / CLOCKS_PER_SEC будет в секундах (по крайней мере, так должно быть). Деление на 1000 превращает это в миллисекунды.
David Young 18.07.2012 00:08:33
Согласно справочному руководству по C, значения clock_t могут меняться, начиная примерно с 36 минут. Если вы измеряете длинные вычисления, вы должны знать об этом.
CyberSkull 9.08.2013 05:03:36
Также помните, что целочисленное деление CLOCKS_PER_SEC / 1000может быть неточным, что может повлиять на конечный результат (хотя в моем опыте CLOCKS_PER_SECвсегда было кратным 1000). Выполнение (1000 * clock()) / CLOCKS_PER_SECменее подвержено неточности деления, но, с другой стороны, более подвержено переполнению. Просто некоторые вопросы для рассмотрения.
Cornstalks 11.09.2013 06:21:10
Разве это не измеряет время процессора, а не время стены?
krs013 1.02.2015 04:53:00

Я всегда использую функцию 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);
}
26
19.12.2008 09:09:42
clock_gettime () не является ANSI C.
PowerApp101 27.05.2009 03:25:17
Также CLOCK_MONOTONIC не реализован во многих системах (включая многие платформы Linux).
Dipstick 9.08.2009 09:13:46
@ PowerApp101 Нет хорошего / надежного способа ANSI C сделать это. Многие из других ответов основаны на POSIX, а не на ANCI C. При этом я считаю, что сегодня. @Dipstick Сегодня я полагаю, что большинство современных платформ [необходима цитата] clock_gettime(CLOCK_MONOTONIC, ...)и даже есть макрос тестирования возможностей _POSIX_MONOTONIC_CLOCK.
omninonsense 31.07.2015 06:07:33

Под окнами:

SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);
-4
11.09.2013 06:11:18
это ANSI C в соответствии с просьбой?
Gyom 27.04.2015 13:10:26

timespec_get от C11

Возвращает до наносекунд, округленных до разрешения реализации.

Похоже, грабеж ANSI от POSIX ' clock_gettime.

Пример: a 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 7.27.2.5 «Функция timespec_get сообщает»:

Если base равен TIME_UTC, для элемента tv_sec задается количество секунд с момента, определенного для реализации, усеченного до целого значения, а для элемента tv_nsec устанавливается целое число наносекунд, округленное до разрешения системных часов. (321)

321) Хотя объект struct timespec описывает времена с наносекундным разрешением, доступное разрешение зависит от системы и может даже превышать 1 секунду.

C ++ 11 также получил std::chrono::high_resolution_clock: C ++ кроссплатформенный таймер высокого разрешения

реализация 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), который является API POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html

    Linux x86-64 имеет clock_gettimeсистемный вызов.

    Обратите внимание, что это не безотказный метод микробенчмаркинга, потому что:

    • man clock_gettimeговорит, что эта мера может иметь разрывы, если вы измените некоторые настройки системного времени во время работы вашей программы. Конечно, это должно быть редкое событие, и вы можете игнорировать его.

    • это измеряет время ожидания, поэтому, если планировщик решит забыть о вашей задаче, он будет работать дольше.

    По этим причинам getrusage()может быть лучше лучший инструмент для тестирования POSIX, несмотря на то, что он обладает максимальной точностью до микросекунды.

    Больше информации на: Измерение времени в Linux - время против часов против getrusage против clock_gettime против gettimeofday против timespec_get?

9
31.10.2018 15:08:09
это правильный ответ с 2017 года, даже MSVC имеет эту функцию; с точки зрения бенчмаркинга, ищите что-то, что читает регистр чипов (более новые версии процессоров x86 с расширениями PT и соответствующие более новые версии ядра Linux / perf)
user755921 28.04.2017 00:01:39

Реализация портативного решения

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

Монотонные часы против отметок времени

Вообще говоря, есть два способа измерения времени:

  • монотонные часы;
  • текущая (дата) отметка времени.

Первый использует монотонный счетчик часов (иногда его называют счетчиком тиков), который подсчитывает тики с заранее определенной частотой, поэтому, если у вас есть значение тиков и частота известна, вы можете легко преобразовать тики в истекшее время. На самом деле не гарантируется, что монотонные часы каким-либо образом отражают текущее системное время, они также могут считать тики с момента запуска системы. Но это гарантирует, что часы всегда будут увеличиваться независимо от состояния системы. Обычно частота связана с аппаратным источником высокого разрешения, поэтому она обеспечивает высокую точность (зависит от аппаратного обеспечения, но у большинства современного оборудования нет проблем с источниками тактового сигнала с высоким разрешением).

Второй способ предоставляет значение времени (дата) на основе текущего значения системных часов. Он также может иметь высокое разрешение, но у него есть один существенный недостаток: на это значение времени могут влиять различные настройки системного времени, например, изменение часового пояса, изменение летнего времени (DST), обновление NTP-сервера, спящий режим системы и т. Д. на. В некоторых случаях вы можете получить отрицательное значение прошедшего времени, которое может привести к неопределенному поведению. На самом деле этот источник времени менее надежен, чем первый.

Таким образом, первое правило в измерении временного интервала - использовать монотонные часы, если это возможно. Он обычно имеет высокую точность и надежен по конструкции.

Резервная стратегия

При реализации переносимого решения стоит учитывать запасную стратегию: использовать монотонные часы, если они доступны, и подход к запасным меткам времени, если в системе нет монотонных часов.

Windows

В 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 « Технические вопросы и ответы» QA1398: «Единицы абсолютного времени Маха», в которой описывается (с примерами кода), как использовать API, специфичный для Маха, для получения монотонных тиков. Существует также локальный вопрос об этом, называемый альтернативой clock_gettime в 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 time_mac.c . Если вам действительно нужна точность в наносекунду, подумайте о том, как я могу использовать mach_absolute_time без переполнения? ,

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.

Солярис и 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).

21
23.05.2017 12:10:11
«нет подходящего решения ANSI с достаточной точностью для задачи измерения времени»: есть C11 timespec_get: stackoverflow.com/a/36095407/895245
Ciro Santilli 冠状病毒审查六四事件法轮功 23.10.2017 07:16:39
Это все еще неправильный способ измерения времени выполнения кода. timespec_getне монотонен
Alexander Saprykin 23.10.2017 10:48:14

Принятый ответ достаточно хорош. Но мое решение более простое. Я просто тестирую в Linux, использую gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.

Alse использование gettimeofday, то tv_secесть часть второй, и tv_usecэто микросекунды , а не миллисекунды .

long currentTimeMillis() {
  struct timeval time;
  gettimeofday(&time, NULL);

  return time.tv_sec * 1000 + time.tv_usec / 1000;
}

int main() {
  printf("%ld\n", currentTimeMillis());
  // wait 1 second
  sleep(1);
  printf("%ld\n", currentTimeMillis());
  return 0;
 }

Это печать:

1522139691342 1522139692342ровно секунду.

2
27.03.2018 08:40:24