Гарантируется ли разрешение 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.

10 85

10 ответов:

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

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

кроме того, посмотрите в .

высокое разрешение, низкое время накладных расходов для процессоров 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);
}

@Bernard:

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

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

Как правило, как это работает:

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

Особые указания:

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

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

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

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

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

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

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

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

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

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

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

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

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

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