Обязательно ли gettimeofday () имеет микросекундное разрешение? - PullRequest
90 голосов
/ 01 августа 2008

Я портирую игру, изначально написанную для 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.

Ответы [ 10 ]

55 голосов
/ 01 августа 2008

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

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

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

40 голосов
/ 02 августа 2008

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

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

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

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

11867927879484732
11867927879692217
it took this long to call printf: 207485

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

#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);
}
18 голосов
/ 04 августа 2008

@ Бернард:

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

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

Обычно это работает так:

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

Особые примечания:

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

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

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

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

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

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

14 голосов
/ 18 августа 2008

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

11 голосов
/ 04 августа 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

9 голосов
/ 02 августа 2008

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

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

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

8 голосов
/ 01 августа 2008

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

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

5 голосов
/ 26 июня 2012

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

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

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

4 голосов
/ 01 августа 2008

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

3 голосов
/ 18 августа 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...