Гарантируется, что 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.

1.08.2008 14:36:18
10 ОТВЕТОВ
РЕШЕНИЕ

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

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

Кроме того, посмотрите на clock_getres()функцию.

56
14.10.2012 12:28:51
Clock_gettime присутствует только в новейшей Linux. другая система имеет только gettimeofday ()
vitaly.v.ch 18.12.2009 16:21:37
@ vitaly.v.ch это POSIX, так что это не только Linux и «newist»? даже корпоративные дистрибутивы, такие как Red Hat Enterprise Linux, основаны на 2.6.18, у которого есть clock_gettime, так что нет, не очень новое .. (дата manpage в RHEL - 2004-March-12, так что это уже давно), если вы не говоря о ДЕЙСТВИТЕЛЬНО БЫСТРОМ СТАРЫМ ядрах WTF ты имеешь ввиду?
Spudd86 14.06.2010 20:32:52
clock_gettime был включен в POSIX в 2001 году. Насколько я знаю, в настоящее время clock_gettime () реализован в Linux 2.6 и qnx. но Linux 2.4 в настоящее время используется во многих производственных системах.
vitaly.v.ch 25.06.2010 12:07:54
Он был введен в 2001 году, но не обязателен до POSIX 2008.
R.. GitHub STOP HELPING ICE 16.01.2011 23:02:29
Из Linux FAQ для lock_gettime (см. Ответ Дэвида Шлоснагла) "CLOCK_MONOTONIC ... настроена частота с помощью NTP через adjtimex (). В будущем (я все еще пытаюсь получить исправление) будет CLOCK_MONOTONIC_RAW, который не будет будет изменен вообще, и будет иметь линейную корреляцию с аппаратными счетчиками. " Я не думаю, что часы _RAW когда-либо превращались в ядро ​​(если только оно не было переименовано в _HR, но мои исследования показывают, что от усилий тоже следует отказаться).
Tony Delroy 15.06.2011 06:16:18

Высокое разрешение и низкая нагрузка на процессоры 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);
}
40
23.08.2010 16:53:06
Обратите внимание, что TSC не всегда может быть синхронизирован между ядрами, может останавливаться или изменять свою частоту, когда процессор переходит в режимы пониженного энергопотребления (и вы не можете этого знать), и в целом не всегда надежно. Ядро способно определить, когда оно надежно, обнаружить другие альтернативы, такие как таймер HPET и ACPI PM, и автоматически выбрать лучший вариант. Рекомендуется всегда использовать ядро ​​для синхронизации, если вы не уверены, что TSC стабильный и монотонный.
CesarB 7.07.2009 23:03:19
TSC на ядре и выше на платформах Intel синхронизируется между несколькими процессорами и увеличивается с постоянной частотой, независимо от состояний управления питанием. См. Intel Software Developer's Manual, Vol. 3 Раздел 18.10. Однако скорость увеличения счетчика не совпадает с частотой процессора. TSC увеличивается на «максимальную разрешенную частоту платформы, которая равна произведению частоты масштабируемой шины и максимального разрешенного отношения шины». Руководство разработчика программного обеспечения Intel, Vol. 3 Раздел 18.18.5. Вы получаете эти значения из специфических для модели регистров ЦП (MSR).
sstock 15.07.2009 07:26:02
Вы можете получить масштабируемую частоту шины и максимальное отношение разрешенных шин, запросив регистры (MSR) для конкретного процессора следующим образом: Масштабируемая частота шины == MSR_FSB_FREQ [2: 0] id 0xCD, Максимальное отношение разрешенных шин == MSR_PLATFORM_ID [12: 8] id 0x17. Обратитесь к Intel SDM Vol.3 Приложение B.1 для интерпретации значений регистра. Вы можете использовать msr-tools в Linux для запроса регистров. kernel.org/pub/linux/utils/cpu/msr-tools
sstock 15.07.2009 07:32:11
Разве ваш код не должен CPUIDснова использоваться после первой RDTSCинструкции и перед выполнением тестируемого кода? Иначе, что мешает выполнению сравнительного кода, выполняемого до / параллельно с первым RDTSC, и, следовательно, недопредставленного в RDTSCдельте?
Tony Delroy 15.06.2011 05:54:36

@Bernard:

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

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

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

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

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

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

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

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

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

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

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

18
15.06.2011 06:44:06
... Люди из ядра пытались заставить людей перестать использовать rdtsc на некоторое время ... и вообще избегать его использования в ядре, потому что это просто ненадежно.
Spudd86 14.06.2010 20:27:25
Для справки, вопрос, который я задал (в отдельном ответе - перед комментариями), звучал так: «Должен признать, большая часть вашего примера прошла прямо у меня над головой. Он компилируется и, похоже, работает. Это безопасно для SMP системы или SpeedStep? "
Bernard 22.01.2011 23:30:01

Вы можете быть заинтересованы в Linux FAQ дляclock_gettime(CLOCK_REALTIME)

14
7.07.2009 23:04:40

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

11
4.08.2008 14:44:56

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

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

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

9
19.12.2013 04:39:01
Так что же происходит в вашем коде, когда gettimeofday () переходит вперед или назад с переходом на летнее время?
mpez0 13.11.2008 13:38:40
Clock_gettime присутствует только в новейшей Linux. другая система имеет только gettimeofday ()
vitaly.v.ch 18.12.2009 16:23:27

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

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

8
12.09.2017 18:12:53

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

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

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

6
23.05.2017 11:54:37

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

4
1.08.2008 15:01:13

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

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

3
7.07.2009 23:05:44
clock_getres(CLOCK_REALTIME)не даст реального разрешения. Он всегда возвращает «1 нс» (одну наносекунду), когда доступны hrtimers, проверьте include/linux/hrtimer.hфайл define HIGH_RES_NSEC 1(подробнее на stackoverflow.com/a/23044075/196561 )
osgx 13.04.2014 14:45:06