Какие альтернативные способы измерения времени существуют в Python для избежания проблем с системным временем?
Что проверяют
Знаете ли вы разницу между настенными часами и монотонными. Следом обычно спрашивают, чем замерить скорость функции и как посчитать таймаут, который не сломается от ручной правки часов или коррекции NTP.
Как отвечать
Разложите часы модуля time по назначению:
time.time(): секунды от эпохи Unix по системным часам. Может прыгнуть вперёд или назад при правке времени или синхронизации. Годится для меток времени, но не для интервалов;time.monotonic(): монотонные часы, назад не идут и правкой системного времени не затрагиваются. Точка отсчёта не определена, смысл имеет только разница двух значений. Выбор для таймаутов, дедлайнов, ограничения частоты и повторных попыток;time.perf_counter(): монотонный счётчик с наибольшим доступным разрешением, время сна включает. Для бенчмарков и замеров длительности;time.process_time(): процессорное время процесса, пользовательское плюс системное, без сна и ожидания ввода-вывода. Для потока естьtime.thread_time();- варианты с
_ns:monotonic_ns()иperf_counter_ns()возвращают целые наносекунды и не теряют точность float на больших значениях; timeit: для микробенчмарков. Сам повторяет замер, по умолчанию берётperf_counterи на время замера отключает сборщик мусора.
В asyncio loop.time() построен на монотонных часах, поэтому таймауты там переводу времени не подвержены.
datetime.now() читает те же системные часы, что time.time(), для интервалов он не годится. Метки храните как aware-время в UTC через datetime.now(timezone.utc), а datetime.utcnow() с Python 3.12 объявлен устаревшим.
Где спотыкаются
Меряют длительность разницей time.time() или двух datetime.now(). После коррекции часов получают отрицательную длительность или таймаут, который сработал сразу.
Сохраняют значение monotonic() в базу или передают в другой процесс. Точка отсчёта у него своя и после перезагрузки меняется, так что для меток между процессами нужны настенные часы в UTC.
Путают process_time() и perf_counter(). Замер сетевого запроса через process_time() покажет почти ноль: процесс ждал ответа, а не считал.