Какие проблемы могут возникнуть при измерении времени выполнения функции с помощью модуля time в Python?
Что проверяют
Отличаете ли вы системные часы от счётчика производительности и понимаете ли, почему один замер ничего не доказывает. Следом обычно спрашивают, как бы вы честно сравнили две реализации.
Как отвечать
Сначала про часы:
time.time(): системное время. Его подводят NTP и администратор, поэтому разность двух значений бывает неточной и даже отрицательной;time.perf_counter(): монотонный счётчик с наибольшим доступным разрешением, сделан именно для замеров. Вариантperf_counter_ns()не теряет точность на float;time.monotonic(): тоже не идёт назад, подходит для таймаутов и дедлайнов;time.process_time(): только процессорное время процесса. Сон и ожидание I/O сюда не попадают, так удобно отделить вычисления от ожидания.
Потом про методику. Один запуск ловит шум: переключение контекста, холодные кеши, ленивые импорты при первом вызове, паузу сборщика мусора. Для микробенчмарков есть timeit: он гоняет код много раз, на время замера отключает сборщик мусора и возвращает суммарное время всех прогонов, а не среднее. timeit.repeat делает несколько серий, и документация советует смотреть на минимум: разброс вверх создают помехи, а не код. Для серьёзного сравнения есть pyperf с отдельными процессами и статистикой разброса, а чтобы понять, где тратится время, нужен профилировщик: cProfile или py-spy.
Где спотыкаются
Замеряют создание ленивого объекта. Генератор, map() или filter() возвращаются мгновенно, работа начинается при итерации, и замер показывает почти ноль.
С корутинами та же ловушка: вызов async-функции только создаёт корутину, мерить нужно время вокруг await, а в асинхронном коде ещё и помнить, что в этот интервал попадает работа других задач.
Сравнивают цифры с разных машин или с ноутбука на батарее, где частота процессора плавает. Сравнивать можно только прогоны в одинаковых условиях, и лучше несколькими сериями.