Гарантировано ли разрешение gettimeofday () в микросекундах?

Я портирую игру, которая изначально была написана для Win32 API, на Linux (ну, портирую OS X порта Win32 на Linux).

Я реализовал QueryPerformanceCounter, указав uSeconds с момента запуска процесса:

BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
    gettimeofday(&currentTimeVal, NULL);
    performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec);
    performanceCount->QuadPart *= (1000 * 1000);
    performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec);

    return true;
}

Это, в сочетании с QueryPerformanceFrequency() заданием константы 1000000 в качестве частоты, хорошо работает на моей машине, давая мне 64-битную переменную, которая содержит uSeconds с момента запуска программы.

Итак, переносится ли это? Я не хочу обнаруживать, что он работает по-другому, если ядро ​​было скомпилировано определенным образом или что-то в этом роде. Однако меня устраивает то, что он не переносится на что-то другое, кроме Linux.


person Bernard    schedule 01.08.2008    source источник


Ответы (10)


Может быть. Но у тебя проблемы посерьезнее. gettimeofday() может привести к неправильному таймингу, если в вашей системе есть процессы, изменяющие таймер (например, ntpd). Однако на "нормальном" Linux разрешение gettimeofday() составляет 10 мкс. Он может перемещаться вперед и назад и, следовательно, во времени, в зависимости от процессов, запущенных в вашей системе. Это фактически дает ответ на ваш вопрос нет.

Вам следует изучить clock_gettime(CLOCK_MONOTONIC), чтобы узнать о временных интервалах. Он страдает от нескольких меньших проблем из-за таких вещей, как многоядерные системы и настройки внешних часов.

Также обратите внимание на функцию clock_getres().

person Louis Brandy    schedule 01.08.2008
comment
clock_gettime присутствует только в новейшем Linux. в другой системе есть только gettimeofday () - person vitaly.v.ch; 18.12.2009
comment
@ vitaly.v.ch это POSIX, так что это не только Linux и «newist»? даже "Enterprise" дистрибутивы, такие как Red Hat Enterprise Linux, основаны на версии 2.6.18, в которой есть clock_gettime, так что нет, не очень новый ... (дата man-страницы в RHEL - 2004-март-12, так что это уже давно), если вы не говорить о ДЕЙСТВИТЕЛЬНО ЛОМАННЫХ СТАРАХ ЯДРАХ WTF ты имеешь в виду? - person Spudd86; 15.06.2010
comment
clock_gettime был включен в POSIX в 2001 году. Насколько мне известно, в настоящее время clock_gettime () реализовано в Linux 2.6 и qnx. но linux 2.4 в настоящее время используется во многих производственных системах. - person vitaly.v.ch; 25.06.2010
comment
Он был введен в 2001 году, но не был обязательным до POSIX 2008. - person R.. GitHub STOP HELPING ICE; 17.01.2011
comment
Из Linux FAQ для lock_gettime (см. Ответ Дэвида Шлоснагла) CLOCK_MONOTONIC ... частота настраивается NTP через adjtimex (). В будущем (я все еще пытаюсь вставить патч) будет CLOCK_MONOTONIC_RAW, который вообще не будет изменен и будет иметь линейную корреляцию с аппаратными счетчиками. Я не думаю, что часы _RAW когда-либо попадали в ядро ​​(если только они не были переименованы в _HR, но мои исследования показывают, что усилия также были оставлены). - person Tony Delroy; 15.06.2011

Высокое разрешение, низкие затраты времени для процессоров Intel

Если вы используете оборудование Intel, вот как прочитать счетчик команд процессора в реальном времени. Он сообщит вам количество циклов процессора, выполненных с момента загрузки процессора. Это, вероятно, самый точный счетчик, который вы можете получить для измерения производительности.

Обратите внимание, что это количество циклов процессора. В Linux вы можете получить скорость процессора из / proc / cpuinfo и разделить, чтобы получить количество секунд. Преобразование этого в двойное очень удобно.

Когда я запускаю это на своей коробке, я получаю

11867927879484732
11867927879692217
it took this long to call printf: 207485

Вот руководство разработчика Intel, в котором содержится множество подробностей.

#include <stdio.h>
#include <stdint.h>

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;
}

main()
{
    unsigned long long x;
    unsigned long long y;
    x = rdtsc();
    printf("%lld\n",x);
    y = rdtsc();
    printf("%lld\n",y);
    printf("it took this long to call printf: %lld\n",y-x);
}
person Mark Harrison    schedule 02.08.2008
comment
Обратите внимание, что TSC не всегда может быть синхронизирован между ядрами, может останавливаться или изменять свою частоту, когда процессор переходит в режимы пониженного энергопотребления (и у вас нет возможности узнать об этом), и в целом он не всегда надежен. Ядро способно определять, когда оно надежно, обнаруживать другие альтернативы, такие как таймер HPET и ACPI PM, и автоматически выбирать лучший из них. Рекомендуется всегда использовать ядро ​​для измерения времени, если вы действительно не уверены, что TSC стабильный и монотонный. - person CesarB; 08.07.2009
comment
TSC на платформах Intel Core и выше синхронизируется между несколькими процессорами и приращениями с постоянной частотой, независимо от состояний управления питанием. См. Руководство разработчика программного обеспечения Intel, Vol. 3 Раздел 18.10. Однако скорость, с которой увеличивается счетчик, не совпадает с частотой процессора. TSC увеличивается на «максимальной разрешенной частоте платформы, которая равна произведению масштабируемой частоты шины и максимального разрешенного коэффициента шины» Intel Software Developer's Manual, Vol. 3 Раздел 18.18.5. Вы получаете эти значения из регистров, зависящих от модели процессора (MSR). - person sstock; 15.07.2009
comment
Вы можете получить масштабируемую частоту шины и максимальное разрешенное соотношение шины, запросив регистры модели ЦП (MSR) следующим образом: Масштабируемая частота шины == MSR_FSB_FREQ [2: 0] id 0xCD, Максимальное разрешенное соотношение шины == MSR_PLATFORM_ID [12: 8] идентификатор 0x17. Обратитесь к Intel SDM Vol.3, приложение B.1, чтобы интерпретировать значения регистров. Вы можете использовать msr-tools в Linux для запроса регистров. kernel.org/pub/linux/utils/cpu/msr-tools - person sstock; 15.07.2009
comment
Разве ваш код не должен снова использовать CPUID после первой RDTSC инструкции и перед выполнением тестируемого кода? В противном случае, что помешает выполнению тестируемого кода до / параллельно с первым RDTSC и, следовательно, недопредставленного в дельте RDTSC? - person Tony Delroy; 15.06.2011

@ Бернард:

Должен признаться, большинство ваших примеров пролетело мимо меня. Он компилируется и, похоже, работает. Это безопасно для систем SMP или SpeedStep?

Хороший вопрос ... Думаю, код в порядке. С практической точки зрения мы используем его в моей компании каждый день, и мы работаем на довольно большом количестве компьютеров, начиная с 2-8 ядер. Конечно, YMMV и т. Д., Но это кажется надежным и низкозатратным (потому что он не переключает контекст в системное пространство) методом синхронизации.

В общем, как это работает:

  • объявить блок кода ассемблерным (и изменчивым, поэтому оптимизатор оставит его в покое).
  • выполнить инструкцию CPUID. Помимо получения некоторой информации о ЦП (с которой мы ничего не делаем), он синхронизирует буфер выполнения ЦП, чтобы на тайминги не влияло выполнение вне очереди.
  • выполнить выполнение rdtsc (метка времени чтения). Это извлекает количество машинных циклов, выполненных с момента перезагрузки процессора. Это 64-битное значение, поэтому при текущей скорости процессора оно будет меняться каждые 194 года или около того. Интересно, что в исходном описании Pentium они отмечают, что он обновляется примерно каждые 5800 лет или около того.
  • последние несколько строк сохраняют значения из регистров в переменных hi и lo и помещают их в 64-битное возвращаемое значение.

Конкретные примечания:

  • выполнение вне очереди может привести к неверным результатам, поэтому мы выполняем инструкцию cpuid, которая помимо предоставления некоторой информации о процессоре также синхронизирует выполнение любой инструкции вне очереди.

  • Большинство ОС синхронизируют счетчики на ЦП при запуске, поэтому ответ будет правильным с точностью до пары наносекунд.

  • Комментарий о гибернации, вероятно, верен, но на практике вы, вероятно, не заботитесь о времени перехода через границы гибернации.

  • относительно скорости: новые процессоры Intel компенсируют изменения скорости и возвращают скорректированный счетчик. Я быстро просмотрел некоторые коробки в нашей сети и нашел только одну коробку, на которой этого не было: Pentium 3, на котором запущен какой-то старый сервер базы данных. (это ящики Linux, поэтому я проверил: grep constant_tsc / proc / cpuinfo)

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

Надеюсь, это удовлетворит ваше любопытство, это интересная и (ИМХО) недостаточно изученная область программирования. Вы знаете, когда Джефф и Джоэл говорили о том, должен ли программист знать C? Я кричал им: «Эй, забудьте про эти высокоуровневые C ... ассемблер - это то, что вам нужно изучить, если вы хотите знать, что делает компьютер!»

person Mark Harrison    schedule 04.08.2008
comment
... Специалисты по ядру пытались убедить людей прекратить использовать rdtsc на некоторое время ... и вообще избегать его использования в ядре, потому что он настолько ненадежен. - person Spudd86; 15.06.2010
comment
Для справки, вопрос, который я задал (в отдельном ответе - перед комментариями), был: я должен признать, что большая часть вашего примера прошла мимо меня. Он компилируется и, похоже, работает. Это безопасно для систем SMP или SpeedStep? - person Bernard; 23.01.2011

Возможно, вас заинтересует FAQ по Linux для clock_gettime(CLOCK_REALTIME)

person David Schlosnagle    schedule 18.08.2008

Wine на самом деле использует gettimeofday () для реализации QueryPerformanceCounter () и, как известно, заставляет многие игры для Windows работать на Linux и Mac.

Запускает http://source.winehq.org/source/dlls/kernel32/cpu.c#L312

ведет к http://source.winehq.org/source/dlls/ntdll/time.c#L448

person Vincent Robert    schedule 04.08.2008

Таким образом, он явно говорит о микросекундах, но говорит, что разрешение системных часов не указано. Я полагаю, что разрешение в этом контексте означает, насколько оно будет увеличено на минимальную величину?

Структура данных определяется как имеющая микросекунды в качестве единицы измерения, но это не означает, что часы или операционная система действительно способны измерять это точно.

Как предполагали другие люди, gettimeofday() - это плохо, потому что установка времени может привести к смещению часов и сбить с толку ваши вычисления. clock_gettime(CLOCK_MONOTONIC) это то, что вы хотите, а clock_getres() сообщит вам точность ваших часов.

person Joe Shaw    schedule 02.08.2008
comment
Так что же происходит в вашем коде, когда gettimeofday () перескакивает вперед или назад с переходом на летнее время? - person mpez0; 13.11.2008
comment
clock_gettime присутствует только в новейшем Linux. в другой системе есть только gettimeofday () - person vitaly.v.ch; 18.12.2009

Фактическое разрешение gettimeofday () зависит от архитектуры оборудования. Процессоры Intel, а также машины SPARC предлагают таймеры с высоким разрешением, измеряющие микросекунды. В других аппаратных архитектурах используется системный таймер, который обычно устанавливается на 100 Гц. В таких случаях разрешение по времени будет менее точным.

Я получил этот ответ от Измерение времени и таймеры с высоким разрешением, Часть I

person CodingWithoutComments    schedule 01.08.2008

В этом ответе упоминаются проблемы с настройкой часов. И ваши проблемы с гарантией тиков, и проблемы с корректировкой времени решены в C ++ 11 с помощью библиотеки <chrono>.

Часы std::chrono::steady_clock гарантированно не будут отрегулированы, и, кроме того, они будут двигаться с постоянной скоростью относительно реального времени, поэтому такие технологии, как SpeedStep, не должны влиять на него.

Вы можете получить типизированные единицы, преобразовав одну из std::chrono::duration специализаций, например std::chrono::microseconds. С этим типом нет двусмысленности в единицах измерения, используемых значением тика. Однако имейте в виду, что часы не обязательно имеют такое разрешение. Вы можете преобразовать продолжительность в аттосекунды, не имея на самом деле точных часов.

person bames53    schedule 26.06.2012

Исходя из моего опыта и того, что я читал в Интернете, ответ - «Нет», это не гарантируется. Это зависит от скорости процессора, операционной системы, версии Linux и т. Д.

person CodingWithoutComments    schedule 01.08.2008

Чтение RDTSC ненадежно в системах SMP, поскольку каждый ЦП поддерживает свой собственный счетчик, и синхронизация каждого счетчика с другим ЦП не гарантируется.

Я могу предложить попробовать clock_gettime(CLOCK_REALTIME). В руководстве по posix указано, что это должно быть реализовано во всех совместимых системах. Он может обеспечивать счетчик наносекунд, но вы, вероятно, захотите проверить clock_getres(CLOCK_REALTIME) в своей системе, чтобы узнать фактическое разрешение.

person Doug    schedule 18.08.2008
comment
clock_getres(CLOCK_REALTIME) не даст реального разрешения. Он всегда возвращает 1 нс (одну наносекунду), когда доступны часы, проверьте include/linux/hrtimer.h файл на define HIGH_RES_NSEC 1 (подробнее см. stackoverflow.com/a/23044075/ 196561) - person osgx; 13.04.2014