6 проверенных способов для измерения скорости программы на Python
easy

6 проверенных способов для измерения скорости программы на Python

Пригодится всем, кто начинает оптимизировать код

Недавно мы решали сложную задачу Эйнштейна с помощью кода на Python, а потом оптимизировали его, чтобы сократить время выполнения. Там всё было просто: с четырёх часов мы оптимизировали время выполнения до долей секунды, и это было явно заметно. Но бывает так, что даже полсекунды оптимизации — это очень хорошо, когда речь идёт о высоконагруженных сервисах. Например, в соцсетях, которыми пользуются сотни тысяч пользователей в минуту. Сегодня мы покажем целых 6 простых способов измерения времени работы кода, которые может использовать каждый.

Как измерять время выполнения кода

В большинстве случаев измерить время работы кода можно так:

  1. Зафиксировать время начала работы.
  2. Зафиксировать время окончания работы.
  3. Вычесть первое значение из второго.

Ещё важно измерять время выполнения кода при одних и тех же условиях:

  • конфигурация и мощность компьютера должны совпадать для всех замеров;
  • загрузка процессора должна быть одинаковой;
  • программа для работы с кодом должна быть одной и той же с одинаковой версией.

Но даже если эти условия выполняются, результаты нескольких замеров могут немного различаться для одного и того же кода. На это могут влиять фоновые процессы, поэтому для точных измерений проводят несколько замеров в чистой среде, когда кроме кода и системных процессов ничего нет. 

Если будете заниматься оптимизацией, вот вам на вырост: нужно различать понятия wall time («время на стене») и процессорное время. Первое показывает прошедшее время от начала до конца работы, второе — время, которое процессор затратил на выполнение кода. Их значения могут различаться, если программа ожидает высвобождение ресурсов для выполнения. Но сейчас можно без этих тонкостей.

Модуль datetime

С помощью такого способа можно измерить время выполнения кода в формате часы:минуты:секунды:микросекунды. Мы использовали модуль datetime, когда оптимизировали код для решения загадки Эйнштейна и ускоряли работу программы более чем в 200 тысяч раз.

# подключаем модуль datetime
import datetime

# фиксируем и выводим время старта работы кода
start = datetime.datetime.now()
print('Время старта: ' + str(start))

# код, время работы которого измеряем

#фиксируем и выводим время окончания работы кода
finish = datetime.datetime.now()
print('Время окончания: ' + str(finish))

# вычитаем время старта из времени окончания
print('Время работы: ' + str(finish - start))

Результат — 51 тысячная секунды. Неплохо, но что покажут другие способы?

Модуль time

Модуль time предоставляет разные возможности для измерения времени работы кода:

  • time.time() поможет измерить время работы в секундах. Если нужно получить время в минутах, результат вычисления нужно разделить на 60, в миллисекундах — умножить на 1000. 
  • time.perf_counter() также можно использовать для измерения времени в секундах, но таймер не будет зависеть от системных часов. Функцию используют, чтобы избежать погрешностей. Функция time.perf_counter_ns() вернёт значение в наносекундах.
  • time.monotonic() подходит для больших программ, поскольку эта функция не зависит от корректировки времени системы. Функция использует отдельный таймер, как и time.perf_counter(), но имеет более низкое разрешение. С помощью time.monotonic_ns() можно получить результат в наносекундах.
  • time.process_time() поможет получить сумму системного и пользовательского процессорного времени в секундах, не включая время сна. Если процесс выполнения блокируется функцией time.sleep() или приостанавливается операционной системой, это время не включается в отчётное. Для наносекунд есть функция time.process_time_ns(), но её поддерживают не все платформы.
  • time.thread_time() сообщит время выполнения текущего потока, а не процесса. Если в коде есть функция time.sleep(), время её выполнения не будет включено.

Time.time(). Давайте посчитаем время выполнения нашего кода с помощью функции time.time() в миллисекундах:

# подключаем модуль time
import time

# фиксируем время старта работы кода
start = time.time()

# код, время работы которого измеряем

#фиксируем время окончания работы кода
finish = time.time()

# вычитаем время старта из времени окончания и получаем результат в миллисекундах
res = finish - start
res_msec = res * 1000
print('Время работы в миллисекундах: ', res_msec)

Получаем результат: 61 тысячная секунды. Результат отличается от предыдущего, тут уже нужно было бы хорошо сделать серию тестов и посчитать среднее значение.

Time.perf_counter(). Посчитаем время выполнения нашего кода с помощью функции time.perf_counter() в секундах:

# подключаем модуль time
import time

# фиксируем время старта работы кода
start = time.perf_counter()

# код, время работы которого измеряем

#фиксируем время окончания работы кода
finish = time.perf_counter()

# вычитаем время старта из времени окончания и выводим результат
print('Время работы: ' + str(finish - start))

Мы получили 51 тысячную секунды — почти такой же результат, как и в самый первый раз. Кажется, что это точное время, но посмотрим, что будет дальше.

Time.monotonic_ns(). Посчитаем время выполнения нашего кода с помощью функции time.monotonic_ns() в наносекундах:

# подключаем модуль time
import time

# фиксируем время старта работы кода
start = time.monotonic_ns()

# код, время работы которого измеряем

#фиксируем время окончания работы кода
finish = time.monotonic_ns()

# вычитаем время старта из времени окончания и получаем результат в наносекундах
print('Время работы в наносекундах: ' + str(finish - start))

Результат примерно такой же — 52 тысячные секунды, но количество цифр в результате меньше, чем в предыдущем случае.

Time.process_time(). Посчитаем сумму системного и пользовательского процессорного времени в секундах:

# подключаем модуль time
import time

# фиксируем время старта работы кода
start = time.process_time()

# код, время работы которого измеряем

#фиксируем время окончания работы кода
finish = time.process_time()

# вычитаем время старта из времени окончания и выводим результат
print('Время работы: ' + str(finish - start))

Время работы снова выросло — с 51 до 62 тысячных секунды. Для одних программ такой разброс вообще некритичен, а для других это может означать, что нужно провести больше тестов.

Time.thread_time(). Наконец, посчитаем время выполнения кода с помощью time.thread_time():

# подключаем модуль time
import time

# фиксируем время старта работы кода
start = time.thread_time()

# код, время работы которого измеряем
#фиксируем время окончания работы кода
finish = time.thread_time()

# вычитаем время старта из времени окончания и выводим результат
print('Время работы: ' + str(finish - start))

Время работы снова выглядит правдоподобно в сравнении с предыдущим результатом. 

Что дальше

В следующий раз продолжим оптимизировать наш код решения задачи Эйнштейна: отформатируем и избавимся от вложенных данных. Подпишитесь, чтобы не пропустить продолжение.

Художник:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Мария Дронова

Соцсети:

Юлия Зубарева

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Вам может быть интересно
easy
[anycomment]
Exit mobile version