time.perf_counter() или time.process_time() для измерения производительности?

#python

#python

Вопрос:

Я понимаю, что time.perf_counter() измеряет общее прошедшее время, даже если процесс в данный момент не запущен. однако time.process_time() измеряет только время фактического выполнения процесса.

Если я просто измеряю производительность функции, какая из этих двух предпочтительнее?

Поскольку меня на самом деле не интересует время, которое мой процессор тратит на другие процессы, я, естественно, подумал, что time.process_time() будет лучше (и стабильнее при разных запусках?) выбор, но имя time.perf_counter(), похоже, предполагает иное.

Пример кода

 import time
from tqdm import trange

start_time_proc = time.process_time()
start_time_perf = time.perf_counter()

tmp = False
for _ in trange(10_000_000):
    tmp = not tmp

elapsed_time_proc = time.process_time() - start_time_proc
elapsed_time_perf = time.perf_counter() - start_time_perf

print("process_time:", elapsed_time_proc)
print("perf_counter:", elapsed_time_perf)
  

https://repl.it/repls/GigaSpryScientists#main.py

Ответ №1:

Обычный способ измерения — использовать счетчик производительности и повторить измерение пару раз.

Время процесса часто не используется, и я могу легко показать вам, почему: time.sleep(1) займет 0 секунд в зависимости от времени процесса. Каждый раз, когда процесс удаляется из планировщика, даже если это связано с тестируемым кодом, часы процесса не продвигаются.

Могу ли я предложить вам взглянуть на встроенный модуль timeit? Он также использует счетчик производительности и может обеспечить более удобный интерфейс для повторного синхронизации.

Редактировать:

Тогда и только тогда, когда ваша функция полностью привязана к процессору, не обращается к внешним ресурсам и не вызывает никаких системных вызовов вообще, process_time является более точным и лучшим выбором.

Комментарии:

1. это зависит от того, какова цель измерения: если кто-то хочет измерить реальное прошедшее время, например, секундомер, тогда действительно вы хотите включить время ожидания и так далее — вы хотите знать, сколько времени требуется для перехода из состояния A в B. В реальных тестах производительности мы целенаправленно исключаем артефакты времени ожидания и планирования операционной системы. (а) если ваша программа не выполняет вычисления, это не влияет на время выполнения программы и (б) sleep время может превышать вычислительное при работе на кластерах и облачных компьютерах — программа работает только в течение доли времени, разделяя процессор с другими пользователями

2. Вы полностью правы, но если ваша функция содержит что-либо, относящееся к ОС, и рассчитывает, что ОС отменит планирование процесса, ваши измерения не будут точными во времени процесса. Это будет точно для расчета процессорного времени, затрачиваемого на кластере, но если ваш вопрос «Сколько времени в среднем требуется для выполнения функции x?», время обработки не отвечает на этот вопрос.

3. Если, например, ваша функция пытается принять мьютекс или ожидает события, пока не наступит тайм-аут в 2 секунды. Какова максимальная продолжительность, которую займет эта функция? Очевидный ответ, который можно было бы ожидать, — это 2 секунды оставшаяся часть t dt (для точности планирования), в то время как во время процесса лучшим, средним и наихудшим временем будет «остаток t», хотя и с меньшим отклонением.

4. ну, в первом комментарии у вас есть точка зрения, а второй не применим (поскольку это предполагает что-то вроде «синхронизации асинхронных событий» — странный термин для использования «синхронизации» или «сравнительного анализа» здесь). Действительно, в этой настройке есть артефакты из связанных с кластером планировщиков, таких как SLURM и SGE, но определение остается в силе: process_time подсчитывает время, прошедшее между 2 вызовами синхронизации для того процесса, в котором процесс не был sleep() . Конечно, ОС разные, но в Linux планирование — это sleep событие, осуществляемое извне в вашем процессе. Таким образом, process_time избавляется от некоторых артефактов.

5. Общее соглашение заключается в минимизации влияния артефакта планировщика на контрольные показатели путем применения «правила больших чисел» и запуска контрольной функции несколько раз и усреднения результата. Кроме того, измерение небольших разделов более надежно для тех артефактов, которые измеряют длинные разделы. Тем не менее, process_time уже уменьшает эти артефакты: напишите небольшой скрипт с вашей функцией, запустите 200 конкурирующих процессов, которые просто подсчитывают переменную, запустите процессы, запустите вашу функцию и время выполнения с обоими модулями. Тогда вы видите разницу.