Профилирование кода — Как найти слабое звено?

Профилирование кода это попытка найти узкие места в вашем коде. Профилирование может найти самые долго выполняющиеся части вашего кода. Найдя их, вы можете оптимизировать эти части удобным вам способом. Python содержит три встроенных профайлера: cProfile, profile и hotshot. В соответствии с документацией Python, hotshot «не поддерживается, и может быть удален из Python». Модуль profile это в корне своем модуль Python, но добавляет много чего сверху в профилированные программы. Поэтому мы сфокусируемся на cProfile, который содержит интерфейс, который имитирует модуль profile.

Профилирование кода при помощи cProfile

Профилирование кода с cProfile это достаточно просто. Все что вам нужно сделать, это импортировать модуль и вызвать его функцию run. Давайте посмотрим на простой пример:

Профилирование кода - Как найти слабое звено?

Здесь мы импортировали модуль hashlib и использовали cProfile для профилирования того, что создал хеш MD5. Первая строка показывает, что в ней 4 вызова функций. Следующая строка говорит нам, в каком порядке результаты выдачи. Здесь есть несколько столбцов.

  • ncalls – это количество совершенных вызовов;
  • tottime – это все время, потраченное в данной функции;
  • percall – ссылается на коэффициент tottime, деленный на ncalls;
  • cumtime – совокупное время, потраченное как в данной функции, так и наследуемых функциях. Это работает также и с рекурсивными функциями!
  • Второй столбец percall – это коэффициент cumtime деленный на примитивные вызовы;
  • filename:lineno(function) предоставляет соответствующие данные о каждой функции.

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

Сохраняем программу как ptest.py. В этом примере мы создали четыре функции. Первые три работают с разными темпами. Быстрая функция запустится с нормальной скоростью, средняя функция потратит примерно полсекунды на запуск, медленная функция потратит примерно три секунды для запуска. Главная функция вызывает остальные три. Давайте запустим cProfile в этой маленькой глупой программе:

Профилирование кода - Как найти слабое звено?

На этот раз мы видим, что у программы ушло 3.5 секунды на запуск. Если вы изучите результаты, то увидите, что cProfile выявил медленную функцию, которая тратит 3 секунды на запуск. Это и есть самая «слабая» часть основной функции. Обычно, когда вы обнаруживаете такие места, вы можете попытаться найти самый быстрый способ выполнения вашего кода, или прийти к выводу, что такая задержка приемлема. В этом примере, мы знаем, что лучший способ ускорить функцию, то убрать вызов time.sleep, или, по крайней мере, снизить продолжительность сна. Вы можете также вызвать cProfile в командной строке, вместо применения в интерпретаторе. Как это сделать:

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Паблик VK

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!

Таким образом, будет запущен cProfile в вашем скрипте по аналогии с тем, как мы делали это ранее. Но что если вам нужно сохранить выдачу профайлера? Что-ж, это очень просто с cProfile! Все что вам нужно, это передать ему команду –o, за которой следует название (или путь) файла выдачи. Вот пример:

К сожалению, выдаваемый файл едва ли можно назвать читаемым. Если вы хотите прочесть файл, тогда вам нужно использовать модуль Python pstats. Вы можете использовать pstats для форматирования выдачи разными способами. Вот небольшой код, который показывает, как получить выдачу, по аналогии с тем, как мы делали это раньше:

Профилирование кода - Как найти слабое звено?

Вызов strip_dirs вырезает все пути к модулям из вывода, пока вызов sort_stats делает сортировку, которая нужна нам для виденья картины. Существует множества очень интересных примеров в документации cProfile, которые наглядно демонстрируют различные пути извлечения информации с использованием модуля pstats.

Выясняем скорость загрузки сайтов

Давайте немного развлечемся. Сделаем небольшой марафон и узнаем какой сайт быстрее всех откроется из нашей программы. Узнать больше насчет производительности requests можно узнать в данной статье.

Результат

Мы получим большой список результатов, давайте найдем только наши функции. Запуск всех сайтов выполнился в 2.68 секунды. Быстрее всех открылся Twitter, самый медленный стал VK. Это конечно не идеальный тест скорости, но наша задача была выполнена. Мы выясняли скорость открытия сайтов используя cProfile.

Подведем итоги

С этого момента вы должны знать, как использовать модуль cProfile для диагностики того, что делает ваш код таким медленным. Вы также можете взглянуть на модуль Python timeit. Он позволяет вам отсчитывать маленькие части вашего кода, если вы не хотите иметь дела со сложностями, которые может повлечь за собой профилирование. Также существует несколько других сторонних модулей, которые также хороши в профилировании как проекты line_profiler и memory_profiler.