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

StackOverflow https://stackoverflow.com/questions/88

Вопрос

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

Я реализовал QueryPerformanceCounter указав количество использованных секунд с момента запуска процесса:

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.

Это было полезно?

Решение

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

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

Кроме того, загляните в clock_getres() функция.

Другие советы

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

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

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

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

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

@Бернард:

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

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

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

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

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

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

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

  • Комментарий о переходе в спящий режим, вероятно, верен, но на практике вас , вероятно, не волнуют тайминги за пределами границ спящего режима.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top