Является ли gettimeofday() гарантией микросекундного разрешения?
Итак, я портирую игру, которая была первоначально написана для Win32 API, для Linux (ну, портируя порт OS X порта Win32 в Linux). Я реализовал QueryPerformanceCounter
, предоставив uSeconds с момента запуска процесса:
BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
gettimeofday(¤tTimeVal, 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
Может быть. Но у вас большие проблемы. gettimeofday()
может привести к неверным таймингам, если в вашей системе есть процессы, которые изменяют таймер (т.е. ntpd). Однако на "нормальном" Linux, я считаю, что разрешение gettimeofday()
равно 10us. Он может двигаться вперед и назад и, следовательно, на основе процессов, выполняемых в вашей системе. Это фактически делает ответ на ваш вопрос "нет".
Вы должны заглянуть в clock_gettime(CLOCK_MONOTONIC)
для временных интервалов. Он имеет несколько меньших проблем из-за таких вещей, как многоядерные системы и внешние настройки часов.
Также рассмотрим функцию clock_getres()
.
Ответ 2
Высокое разрешение, низкое временное время для процессоров 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);
}
Ответ 3
@Bernard:
Я должен признать, что большая часть вашего примера прошла прямо над моей головой. Он компилируется и, похоже, работает. Безопасно ли это для систем SMP или SpeedStep?
Это хороший вопрос... Я думаю, что код в порядке.
С практической точки зрения, мы используем его в моей компании каждый день,
и мы запускаем довольно широкий набор ящиков, все от 2-8 ядер.
Конечно, YMMV и т.д., Но, похоже, это надежный и низкозатратный
(потому что он не делает переход контекста в системное пространство)
времени.
Как правило, как это работает:
- объявить блок кода ассемблером (и нестабильным, поэтому
оптимизатор оставит его в покое).
- выполнить инструкцию CPUID. В дополнение к получению некоторой информации о процессоре
(с которым мы ничего не делаем) он синхронизирует буфер выполнения ЦП
так что на тайминги не влияет выполнение вне очереди.
- выполнить выполнение rdtsc (чтение метки времени). Это выбирает количество
машинные циклы, выполненные с процессором reset. Это 64-разрядный
так что при текущих скоростях процессора он будет обтекаться каждые 194 года или около того.
Интересно, что в исходной ссылке Pentium они отмечают, что она обтекает каждый
5800 лет или около того.
- последние пару строк сохраняют значения из регистров в
переменные hi и lo, и поместите это в 64-битное возвращаемое значение.
Конкретные примечания:
-
Выполнение вне порядка может привести к неправильным результатам, поэтому мы выполняем
инструкция "cpuid", которая в дополнение к предоставлению вам некоторой информации
о процессоре также синхронизируется выполнение любой команды вне порядка.
-
Большинство ОС синхронизируют счетчики на процессорах при их запуске, поэтому
ответ хорош в течение нескольких наносекунд.
-
Спящий комментарий, вероятно, верен, но на практике вы
вероятно, не заботятся о таймингах по границам гибернации.
-
относительно скорости: новые процессоры Intel компенсируют скорость
изменяет и возвращает скорректированный счет. Я быстро просмотрел
некоторые из ящиков в нашей сети и обнаружили только одну коробку, которая
не было: Pentium 3 работает на каком-то старом сервере базы данных.
(это ящики linux, поэтому я проверил с помощью: grep constant_tsc/proc/cpuinfo)
-
Я не уверен в процессорах AMD, мы в первую очередь магазин Intel,
хотя я знаю, что некоторые из наших низкоуровневых системных гуру сделали
Оценка AMD.
Надеюсь, это удовлетворит ваше любопытство, это интересно и (ИМХО)
недостаточно изученная область программирования. Вы знаете, когда Джефф и Джоэл были
говорить о том, должен ли программист знать C? я был
крича на них: "Эй, забудь, что на самом высоком уровне C... ассемблер
это то, чему вы должны научиться, если хотите узнать, что такое компьютер.
делать! "
Ответ 4
Вам может быть интересно FAQ по Linux для clock_gettime(CLOCK_REALTIME)
Ответ 5
Вино фактически использует 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
Ответ 6
Итак, он говорит о микросекундах явно, но говорит, что разрешение системных часов не указано. Я полагаю, что разрешение в этом контексте означает, как наименьшая сумма будет когда-либо увеличена?
Структура данных определяется как имеющая микросекунды как единицу измерения, но это не означает, что часы или операционная система действительно способны точно измерять.
Как и другие люди, gettimeofday()
плохо, потому что установка времени может вызвать перекос часов и сбросить ваши вычисления. clock_gettime(CLOCK_MONOTONIC)
- это то, что вы хотите, и clock_getres()
сообщит вам о точности ваших часов.
Ответ 7
Фактическое разрешение gettimeofday() зависит от аппаратной архитектуры. Процессоры Intel, а также машины SPARC предлагают таймеры с высоким разрешением, которые измеряют микросекунды. Другие аппаратные архитектуры возвращаются к системному таймеру, который обычно устанавливается на 100 Гц. В таких случаях временное разрешение будет менее точным.
Я получил этот ответ от Измерение времени и времени высокого разрешения, часть I
Ответ 8
В этом ответе упоминаются проблемы с настраиваемыми часами. Обе проблемы, гарантирующие блоки тика и проблемы с настраиваемым временем, решаются на С++ 11 с помощью библиотеки <chrono>
.
Гарантируется, что часы std::chrono::steady_clock
не будут скорректированы, и, кроме того, он будет продвигаться с постоянной скоростью относительно реального времени, поэтому такие технологии, как SpeedStep, не должны влиять на него.
Вы можете получить типы типов, перейдя на одну из специализированных std::chrono::duration
, например std::chrono::microseconds
. При таком типе не существует двусмысленности относительно единиц, используемых значением галочки. Однако имейте в виду, что часы не обязательно имеют такое разрешение. Вы можете преобразовать длительность в аттосекунды, не имея фактически точных часов.
Ответ 9
Из моего опыта и из того, что я прочитал через Интернет, ответ "Нет", это не гарантируется. Это зависит от скорости процессора, операционной системы, особенностей Linux и т.д.
Ответ 10
Чтение RDTSC не является надежным в системах SMP, поскольку каждый процессор поддерживает свой собственный счетчик, и каждый счетчик не гарантируется синхронизацией с другим ЦП.
Я могу предложить попробовать clock_gettime(CLOCK_REALTIME)
. Руководство posix указывает, что это должно быть реализовано на всех совместимых системах. Он может обеспечить количество наносекунд, но вы, вероятно, захотите проверить clock_getres(CLOCK_REALTIME)
на вашей системе, чтобы узнать, что такое фактическое разрешение.