Ответ 1
Существует следующий файл:
<mach/mach_time.h>
В этом файле вы найдете функцию с именем mach_absolute_time()
. mach_absolute_time()
возвращает номер uint64, который не имеет определенного значения. Представьте, что это тики, но нигде не определено, как долго один тик. Определяются только четыре вещи:
-
mach_absolute_time()
возвращает число "тиков" с момента последней загрузки. - При каждой загрузке счетчик тиков начинается с нуля.
- Счетчик счетчиков строго строит вверх (он никогда не идет назад).
- Счетчик тиков учитывает только тики во время работы системы.
Как вы можете видеть, счетчик тиков несколько отличается от обычных системных часов. Прежде всего, системные часы не начинаются с нуля при загрузке системы, а в наилучшем приближении системы к текущему "времени настенных часов". Обычные системные часы также не работают строго вверх, например. системные часы могут быть раньше времени, и система регулярно синхронизирует системное время с использованием NTP (Network Time Protocol). Если система заметила, что на следующей синхронизации NTP она отстает на две секунды, она вернет системные часы на две секунды, чтобы исправить ошибку. Это регулярно ломает программное обеспечение, потому что многие программисты полагаются на то, что системное время никогда не отскакивает назад; но это так, и это разрешено. Последнее различие заключается в том, что нормальное системное время не остановится во время сна, но счетчик тиков не будет увеличиваться, пока система спала. Когда система снова просыпается, это всего лишь пара тиков до того, как она заснула.
Итак, как вы преобразовываете эти тики в реальное значение времени?
Файл выше также определяет структуру с именем mach_timebase_info
:
struct mach_timebase_info {
uint32_t numer;
uint32_t denom;
};
Вы можете получить правильные значения для этой структуры, используя функцию mach_timebase_info()
, например.
kern_return_t kerror;
mach_timebase_info_data_t tinfo;
kerror = mach_timebase_info(&tinfo);
if (kerror != KERN_SUCCESS) {
// TODO: handle error
}
KERN_SUCCESS
(и возможные коды ошибок) определены в
<mach/kern_return.h>
Очень маловероятно, чтобы эта функция возвращала ошибку, и KERN_SUCCESS
равна нулю, поэтому вы также можете напрямую проверить, что kerror
не равен нулю.
Как только вы получили информацию в tinfo
, вы можете использовать ее для вычисления "коэффициента преобразования", если вы хотите преобразовать это число в единицу времени:
double hTime2nsFactor = (double)tinfo.numer / tinfo.denom;
Отбрасывая первое число на double
, GCC автоматически добавляет второе число в double
, и результат также будет double
. Зная этот фактор, который, по-видимому, равен 1.0 на машинах Intel, но он может быть совсем другим на машинах PPC (и, возможно, он отличается от ARM), довольно просто преобразовать время хоста в наносекунды и наносекунды для размещения времени.
uint64_t systemUptimeNS = (uint64_t)(mach_absolute_time() * hTime2nsFactor);
systemUptimeNS содержит количество наносекунд, которые система запускала (не спала) между последней загрузкой и теперь. Если вы разделите это время на наносекунды таким коэффициентом, вы получите количество тиков. Это может быть очень полезно для функции mach_wait_until()
. Предположим, вы хотите, чтобы текущий поток спал в течение 800 наносекунд. Вот как вы это сделаете:
uint64_t sleepTimeInTicks = (uint64_t)(800 / hTime2nsFactor);
mach_wait_until(mach_absolute_time() + sleepTimeInTicks);
Маленький совет: Если вам регулярно нужно преобразовывать значения времени в тики, обычно (в зависимости от процессора) быстрее умножаться, чем делиться:
double ns2HTimeFactor = 1.0 / hTime2nsFactor;
Теперь вы можете умножить на ns2HTimeFactor
вместо деления на hTime2nsFactor
.
Конечно, это пустая трата времени, чтобы пересчитать коэффициенты каждый раз, когда вы в них нуждаетесь. Эти факторы постоянны, они никогда не изменятся во время работы системы. Таким образом, вы можете рассчитать их где-то рядом с началом приложения и сохранить их до тех пор, пока приложение не закроется.
В Cocoa я бы рекомендовал написать статический класс для всего выше. Вы можете рассчитать коэффициенты преобразования для любого преобразования в методе +(void)initialize
класса. Cocoa гарантирует, что этот метод, безусловно, будет автоматически выполнен до того, как какое-либо сообщение будет отправлено этому классу, оно наверняка выполняется только один раз во время выполнения приложения, и оно, безусловно, выполняется поточно-безопасным образом, t нужно беспокоиться о блокировке/синхронизации или атомных операциях.