Как вы можете профилировать скрипт Python?

Project Euler и другие конкурсы кодирования часто имеют максимальное время для проведения или люди хвастаются тем, насколько быстро они решение запускается. В Python подходы иногда несколько беспорядочные - например, добавление кода синхронизации в __main__.

Как хорошо определить, сколько времени требуется для запуска программы Python?


person Chris Lawlor    schedule 24.02.2009    source источник
comment
Программы Project Euler не нуждаются в профилировании. Либо у вас есть алгоритм, который работает менее чем за минуту, либо у вас совершенно неправильный алгоритм. Тюнинг бывает уместен редко. Обычно вы должны использовать свежий подход.   -  person S.Lott    schedule 24.02.2009
comment
С.Лотт: Профилирование часто помогает определить, какие подпрограммы работают медленно. Подпрограммы, которые занимают много времени, являются отличными кандидатами на улучшение алгоритмов.   -  person stalepretzel    schedule 14.09.2012
comment
Стоит упомянуть два пакета: py-spy и nvtx для случаев. когда код работает на процессорах и / или графических процессорах.   -  person 0x90    schedule 12.03.2021
comment
@ 0x90 py-spy (профилировщик выборки) заслуживает отдельного ответа.   -  person jfs    schedule 18.06.2021


Ответы (30)


Python включает профилировщик под названием cProfile. Он не только дает общее время работы, но также время каждой функции в отдельности и сообщает вам, сколько раз каждая функция была вызвана, что упрощает определение того, где вы должны сделать оптимизацию.

Вы можете вызвать его из своего кода или из интерпретатора, например:

import cProfile
cProfile.run('foo()')

Более того, вы можете вызвать cProfile при запуске скрипта:

python -m cProfile myscript.py

Чтобы упростить задачу, я сделал небольшой командный файл под названием profile.bat:

python -m cProfile %1

Так что все, что мне нужно сделать, это запустить:

profile euler048.py

И я получаю вот что:

1007 function calls in 0.061 CPU seconds

Ordered by: standard name
ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000    0.061    0.061 <string>:1(<module>)
 1000    0.051    0.000    0.051    0.000 euler048.py:2(<lambda>)
    1    0.005    0.005    0.061    0.061 euler048.py:2(<module>)
    1    0.000    0.000    0.061    0.061 {execfile}
    1    0.002    0.002    0.053    0.053 {map}
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler objects}
    1    0.000    0.000    0.000    0.000 {range}
    1    0.003    0.003    0.003    0.003 {sum}

РЕДАКТИРОВАТЬ: обновлена ​​ссылка на хороший видеоресурс с PyCon 2013 под названием Профилирование Python
Также через YouTube.

person Chris Lawlor    schedule 24.02.2009
comment
Также полезно отсортировать результаты, это можно сделать с помощью переключателя -s, например: '-s time'. Вы можете использовать кумулятивные варианты сортировки / по имени / по времени / по файлу. - person Jiri; 25.02.2009
comment
К сожалению, вы не можете отсортировать перкал ни по общему, ни по совокупному времени, что является серьезным недостатком IMO. - person Joe Shaw; 17.12.2009
comment
Также прочтите документацию по python здесь, это неплохо - person Cosmin Lehene; 01.11.2012
comment
Также стоит отметить, что вы можете использовать модуль cProfile из ipython с помощью волшебной функции% prun (запуск профиля). Сначала импортируйте свой модуль, а затем вызовите основную функцию с помощью% prun: import euler048; % prun euler048.main () - person RussellStewart; 31.03.2014
comment
Для визуализации дампов cProfile (созданных python -m cProfile -o <out.profile> <script>) бесценен RunSnakeRun, вызываемый как runsnake <out.profile>. - person Lily Chung; 05.05.2014
comment
Этот ответ, вероятно, следует обновить для python 3: просто используйте profile. - person Neil G; 24.10.2014
comment
Если мой код не прерывается, можно ли как-нибудь сократить время для вашего профилировщика? - person Naman; 17.11.2014
comment
@NeilG даже для python 3 по-прежнему рекомендуется cprofile более profile. - person trichoplax; 04.01.2015
comment
чтобы добавить к ответу, если вам нужно запустить его из кода и сгенерировать выходной файл, вы можете использовать import cProfile cProfile.run ('foo ()', 'somefilename') - person Alex Punnen; 20.03.2016
comment
например, в профиле печатается {map} или {xxx}. как мне узнать, из какого файла вызывается метод {xxx}? мой профиль печатает {метод 'compress' объектов 'zlib.Compress'} занимает большую часть времени, но я не использую zlib, поэтому я предполагаю, что некоторые функции call numpy могут его использовать. Как узнать, какой именно файл и строка занимает много времени? - person machen; 28.10.2017
comment
Другой недостаток cProfile заключается в том, что невозможно сделать что-либо кроме, кроме вывода вывода на консоль. Если вы хотите зафиксировать тайминги / сохранить их в системе метрик / для отчетности позже, вам придется вручную анализировать строки вывода консоли. - person Basic; 18.12.2018
comment
Я обычно считаю, что cumtime - это то, что вас действительно волнует (это то, что стоит оптимизировать) для простоты использования: python -m cProfile -s cumtime pyname.py - person borgr; 26.07.2019
comment
Если вы хотите запустить модуль вместо скрипта, просто снова используйте флаг -m. Пример: python -m cProfile -m myModule - person cowlinator; 07.04.2020
comment
@cowlinator, какая разница; не все скрипты импортируются как модули? Или cProfile также профилирует определения функций? - person theX; 25.06.2020
comment
Все сценарии можно импортировать как модули, но не все модули можно запускать как сценарии. Если python mymodule.py выйдет из строя, потому что для этого требуется python -m mymodule, тогда python -m cProfile mymodule.py также выйдет из строя. Так что используйте python -m cProfile -m mymodule. - person cowlinator; 26.06.2020
comment
Вот так должен выглядеть ответ в Stackoverflow - person ShifraSec; 11.02.2021

Некоторое время назад я сделал pycallgraph, который генерирует визуализацию из вашего кода Python. Изменить: я обновил пример для работы с 3.3, последней версией на момент написания этой статьи.

После pip install pycallgraph и установки GraphViz вы можете запустить его из командной строки:

pycallgraph graphviz -- ./mypythonscript.py

Или вы можете профилировать определенные части вашего кода:

from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput

with PyCallGraph(output=GraphvizOutput()):
    code_to_profile()

Любой из них создаст файл pycallgraph.png, подобный изображению ниже:

введите описание изображения здесь

person gak    schedule 06.08.2012
comment
Вы раскрашиваете по количеству звонков? Если это так, вы должны раскрашивать по времени, потому что функция с наибольшим количеством вызовов не всегда занимает больше всего времени. - person red; 06.08.2013
comment
@red Вы можете настроить цвета по своему усмотрению и даже независимо для каждого измерения. Например, красный для звонков, синий для времени, зеленый для использования памяти. - person gak; 07.08.2013
comment
@ Джеральд, это не сработает. Да Pip install, хорошо устанавливает, но он говорит, что не может найти graphviz, когда я запускаю команду. Я даже apt-get install graphviz сделал, без разницы. запуск терминала с помощью «graphviz» показывает, что команда не найдена. Что я делаю неправильно? Я использую Linux Mint 15. - person Matt; 29.11.2013
comment
@Matt Опубликуйте отчет об ошибке в системе отслеживания проблем с pycallgraph. Stackoverflow - не место для поддержки pycallgraph :) А пока я скачаю Mint, чтобы протестировать его для вас. - person gak; 30.11.2013
comment
@Matt, если вы устанавливаете mint поверх Ubuntu grub, что-то испортит ваш /etc/sources.list. Рассмотрим LMDE. - person Heath Hunnicutt; 13.12.2013
comment
получение этой ошибкиTraceback (most recent call last): /pycallgraph.py", line 90, in generate output.done() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 94, in done source = self.generate() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 143, in generate indent_join.join(self.generate_attributes()), File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 169, in generate_attributes section, self.attrs_from_dict(attrs), ValueError: zero length field name in format - person Ciasto piekarz; 18.08.2014
comment
По какой-то причине у меня это работает, если я использую его как pycallgraph - ./mypythonscript.py, аргумент graphviz кажется избыточным. - person Johan; 03.11.2014
comment
Я обновил это, чтобы упомянуть, что вам нужно установить GraphViz, чтобы все работало, как описано. В Ubuntu это просто sudo apt-get install graphviz. - person mlissner; 18.11.2015
comment
Для установки потребуется немного поработать, вот 3 шага, которые помогут. 1. Установите через pip, 2. Установите GraphViz через exe 3. Установите переменные пути в каталог GraphViz 4. Выясните, как исправить все другие ошибки. 5. Разберитесь, где он сохраняет файл png? - person marsh; 24.03.2016
comment
Я обнаружил, что графы вызовов полезны только для ограниченного числа узлов; реальные программы, которые я использую, обычно занимают огромную площадь и делают рисунок непонятным. Я бы предпочел использовать графики пламени. - person Jason S; 15.04.2017
comment
На странице github говорится, что этот проект заброшен ... :( - person A. Rabus; 12.11.2019

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

Если вы также хотите профилировать потоки, вам нужно посмотреть на _ 1_ function в документации.

Вы также можете создать для этого свой собственный threading.Thread подкласс:

class ProfiledThread(threading.Thread):
    # Overrides threading.Thread.run()
    def run(self):
        profiler = cProfile.Profile()
        try:
            return profiler.runcall(threading.Thread.run, self)
        finally:
            profiler.dump_stats('myprofile-%d.profile' % (self.ident,))

и используйте этот класс ProfiledThread вместо стандартного. Это могло бы дать вам больше гибкости, но я не уверен, что оно того стоит, особенно если вы используете сторонний код, который не будет использовать ваш класс.

person Joe Shaw    schedule 17.12.2009
comment
Я также не вижу ссылок на runcall в документации. Взглянув на cProfile.py, я не уверен, почему вы используете функцию threading.Thread.run или self в качестве аргумента. Я ожидал увидеть здесь ссылку на метод выполнения другого потока. - person PypeBros; 09.11.2011
comment
Этого нет в документации, но он есть в модуле. См. hg.python.org/cpython/file/6bf07db23445/Lib. /cProfile.py#l140. Это позволяет вам профилировать вызов конкретной функции, и в нашем случае мы хотим профилировать функцию target потока, которую выполняет вызов threading.Thread.run(). Но, как я сказал в ответе, вероятно, не стоит создавать подкласс Thread, поскольку любой сторонний код не будет его использовать, а вместо этого использовать threading.setprofile(). - person Joe Shaw; 09.11.2011
comment
упаковка кода с помощью profiler.enable () и profiler.disable (), похоже, тоже работает неплохо. Это в основном то, что делает runcall, и он не требует каких-либо аргументов или подобных вещей. - person PypeBros; 10.11.2011
comment
Я объединил свой собственный stackoverflow.com/questions/ 10748118 / с ddaa.net/blog/python/lsprof-calltree и вроде работает;! -) - person Dima Tisnek; 11.07.2012
comment
Джо, ты знаешь, как профилировщик работает с asyncio в Python 3.4? - person Nick Chammas; 18.06.2015
comment
@NickChammas Понятия не имею, извини. Я предполагаю, что внутренности кода asyncio написаны на C, поэтому профилировщик не сможет их коснуться. - person Joe Shaw; 18.06.2015
comment
Возможно, важно отметить, что такой подход не работает для распараллеливания с использованием многопроцессорности (эффективно многоядерного, преодолевающего глобальную блокировку интерпретатора). Несмотря на то, что многопроцессорность воспроизводит большинство API-интерфейсов модуля потоковой передачи, в документе явно упоминается, что threading.setprofile() их не имеет. Цитата из doc: multiprocessing не содержит аналогов [...] threading.setprofile() - person jmon12; 10.03.2020

Вики-сайт python - отличная страница для профилирования ресурсов: http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code

как и документы python: http://docs.python.org/library/profile.html

Как показал Крис Лоулор, cProfile - отличный инструмент, который можно легко использовать для печати на экране:

python -m cProfile -s time mine.py <args>

или в файл:

python -m cProfile -o output.file mine.py <args>

PS> Если вы используете Ubuntu, обязательно установите python-profile

apt-get install python-profiler 

Если вы выводите в файл, вы можете получить хорошую визуализацию, используя следующие инструменты

PyCallGraph: инструмент для создания изображений графа вызовов
установка:

 pip install pycallgraph

запустить:

 pycallgraph mine.py args

Посмотреть:

 gimp pycallgraph.png

Вы можете использовать все, что хотите, для просмотра файла png, я использовал gimp
К сожалению, я часто получаю

точка: график слишком велик для растровых изображений cairo-renderer. Масштабирование на 0,257079 для соответствия

что делает мои изображения необычно маленькими. Поэтому я обычно создаю файлы svg:

pycallgraph -f svg -o pycallgraph.svg mine.py <args>

PS> обязательно установите graphviz (который предоставляет программу с точками):

pip install graphviz

Альтернативное построение графиков с использованием gprof2dot через @maxy / @quodlibetor:

pip install gprof2dot
python -m cProfile -o profile.pstats mine.py
gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg
person brent.payne    schedule 08.10.2011
comment
gprof2dot тоже может делать эти графики. Я думаю, что результат получился немного лучше (пример). - person maxy; 13.05.2012
comment
graphviz также требуется, если вы используете OSX - person Vaibhav Mishra; 30.01.2014
comment
Проект был заархивирован на github и, похоже, больше не поддерживается. github.com/gak/pycallgraph - person dre-hh; 24.02.2021

Комментарий @ Maxy к этому ответу помог мне настолько, что я думаю, что он заслуживает отдельного ответа: у меня уже был cProfile-сгенерированный. pstats, и я не хотел повторно запускать что-то с pycallgraph, поэтому я использовал gprof2dot, и получились довольно svgs:

$ sudo apt-get install graphviz
$ git clone https://github.com/jrfonseca/gprof2dot
$ ln -s "$PWD"/gprof2dot/gprof2dot.py ~/bin
$ cd $PROJECT_DIR
$ gprof2dot.py -f pstats profile.pstats | dot -Tsvg -o callgraph.svg

и БАМ!

Он использует точку (то же самое, что и pycallgraph), поэтому вывод выглядит одинаково. Мне кажется, что gprof2dot теряет меньше информации:

Пример вывода gprof2dot

person quodlibetor    schedule 11.12.2012
comment
Хороший подход, работает очень хорошо, так как вы можете просматривать SVG в Chrome и т. Д. И масштабировать его вверх / вниз. В третьей строке есть опечатка, она должна быть: ln -s pwd / gprof2dot / gprof2dot.py $ HOME / bin (или используйте ln -s $ PWD / gprof2dot / gprof2dot.py ~ / bin в большинстве оболочек - серьезный акцент используется в качестве форматирования в первая версия). - person RichVel; 04.01.2013
comment
Ах, хорошее замечание. Я почти каждый раз ошибаюсь в порядке аргументов ln. - person quodlibetor; 04.01.2013
comment
Хитрость заключается в том, чтобы помнить, что ln и cp имеют одинаковый порядок аргументов - подумайте об этом как о «копировании file1 в file2 или dir2, но с созданием ссылки» - person RichVel; 04.01.2013
comment
В этом есть смысл, я думаю, что использование TARGET на странице руководства сбивает меня с толку. - person quodlibetor; 04.01.2013
comment
Пожалуйста, а как получилось скруглить углы? Я чувствую, что это улучшает читаемость. Я получаю просто уродливые острые углы, что не круто при наличии большого количества краев вокруг коробок. - person Hibou57; 10.09.2014
comment
Извините, @ Hibou57 это просто вывод по умолчанию для меня. Может быть, попробовать перейти на более раннюю или более раннюю версию dot? - person quodlibetor; 10.09.2014
comment
Отличное решение! Сначала я подумал, что инструмент бесполезен, поскольку он показывает только 5 вызовов, но чтобы показать все, добавьте -n0 -e0 к аргументам. - person marsh; 29.03.2016
comment
Спасибо @quodlibetor! В Win 10, в зависимости от установки conda или pip, редактор командной строки может заявить, что dot не распознается. Установка PATH для точки не рекомендуется, например. согласно github.com/ContinuumIO/anaconda-issues/issues/1666. Вместо этого можно использовать полный путь точки graphviz, например: i) python -m cProfile -o profile.pstats main.py ii) gprof2dot -f pstats profile.pstats | "C:\Program Files (x86)\Graphviz2.38\bin\dot.exe" -Tsvg -o gprof2dot_pstats.svg. - person Sven Haile; 22.05.2019

Самый простой и самый быстрый способ узнать, куда идет все время.

1. pip install snakeviz

2. python -m cProfile -o temp.dat <PROGRAM>.py

3. snakeviz temp.dat

Рисует круговую диаграмму в браузере. Самая большая часть - это проблемная функция. Очень простой.

person CodeCabbie    schedule 08.03.2018
comment
См. Также ответ zaxliu, в котором есть ссылка на инструмент и пример вывода. - person Melebius; 08.06.2020
comment
Используя это в Windows, создал сценарий летучей мыши для интеграции pycharm, он работает как шарм! Спасибо - person Andrea; 25.11.2020

При исследовании этой темы я столкнулся с удобным инструментом под названием SnakeViz. SnakeViz - это веб-инструмент для визуализации профилей. Его очень легко установить и использовать. Обычно я использую его для создания файла статистики с %prun и последующего анализа в SnakeViz.

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

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

введите здесь описание изображения

person zaxliu    schedule 25.05.2016
comment
Ответ CodeCabbie включает (короткие) инструкции по установке и показывает, как (легко) использовать SnakeViz. - person Oren Milman; 18.08.2019
comment
Здесь я прочитал IMHO хорошее руководство по использованию профилирования для Python в jupyter notebook: к науке о науке. ru / speed-up-jupyter-notebooks-20716cbe2025 - person Alexei Martianov; 04.09.2019

cProfile отлично подходит для профилирования, а _ 2_ отлично подходит для визуализации результатов. pyprof2calltree между ними обрабатывает преобразование файлов.

python -m cProfile -o script.profile script.py
pyprof2calltree -i script.profile -o script.calltree
kcachegrind script.calltree

Необходимые системные пакеты:

  • kcachegrind (Linux), qcachegrind (MacOs)

Настройка в Ubuntu:

apt-get install kcachegrind 
pip install pyprof2calltree

Результат:

Скриншот результата

person Federico    schedule 11.05.2016
comment
Пользователи Mac устанавливают brew install qcachegrind и заменяют каждый kcachegrind на qcachegrind в описании для успешного профилирования. - person Kevin Katzke; 17.12.2017
comment
Мне пришлось сделать это, чтобы заставить его работать: export QT_X11_NO_MITSHM=1 - person Yonatan Simson; 04.12.2019
comment
Из множества решений, перечисленных здесь: это лучше всего работает с большими данными профиля. gprof2dot не является интерактивным и не имеет общего времени процессора (только относительный процент) tuna и snakeviz умирают на большем профиле. pycallgraph заархивирован и больше не поддерживается - person dre-hh; 24.02.2021
comment
@YonatanSimson Вы, вероятно, запускаете kcachegrind в контейнере докеров, который по умолчанию не использует IPC для хоста. Другой способ исправить это - запустить контейнер докеров с --ipc=host. - person Maxim Egorushkin; 26.05.2021

Недавно я создал tuna для визуализации среды выполнения Python и профилей импорта; здесь это может быть полезно.

введите описание изображения здесь

Установить с помощью

pip install tuna

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

python3 -m cProfile -o program.prof yourfile.py

или профиль импорта (требуется Python 3.7+)

python3 -X importprofile yourfile.py 2> import.log

Тогда просто запустите тунец в файле

tuna program.prof
person Nico Schlömer    schedule 04.08.2018

Также стоит упомянуть программу просмотра дампа cProfile с графическим интерфейсом пользователя RunSnakeRun. Это позволяет вам сортировать и выбирать, тем самым увеличивая масштабирование соответствующих частей программы. Размеры прямоугольников на картинке пропорциональны затраченному времени. Если вы наведете указатель мыши на прямоугольник, он выделит этот вызов в таблице и повсюду на карте. Когда вы дважды щелкаете по прямоугольнику, он увеличивает масштаб этой части. Он покажет вам, кто называет эту часть и что называет эта часть.

Описательная информация очень полезна. Он показывает вам код для этого бита, который может быть полезен, когда вы имеете дело со встроенными вызовами библиотеки. Он сообщает вам, в каком файле и в какой строке найти код.

Также хочу указать на то, что ОП сказал «профилирование», но, похоже, он имел в виду «время». Имейте в виду, что при профилировании программы будут работать медленнее.

введите описание изображения здесь

person Pete    schedule 22.02.2015

pprofile

line_profiler (уже представленный здесь) также вдохновил pprofile, который описывается как:

Гранулярность строк, детерминированный и статистический профилировщик на чистом Python

Он обеспечивает линейную детализацию как line_profiler, является чистым Python, может использоваться как отдельная команда или модуль и даже может генерировать файлы в формате callgrind, которые можно легко проанализировать с помощью [k|q]cachegrind.

впроф

Существует также vprof, пакет Python, описанный как:

[...] обеспечение богатой и интерактивной визуализации различных характеристик программ Python, таких как время работы и использование памяти.

тепловая карта

person BenC    schedule 02.03.2015

Хорошим модулем профилирования является line_profiler (вызывается с помощью скрипта kernprof.py). Его можно загрузить здесь.

Насколько я понимаю, cProfile дает информацию только об общем времени, затраченном на каждую функцию. Таким образом, отдельные строки кода не рассчитаны по времени. Это проблема научных вычислений, поскольку часто одна строка может занять много времени. Кроме того, насколько я помню, cProfile не улавливал время, которое я тратил, скажем, на numpy.dot.

person Ian Langmore    schedule 20.10.2011

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

Я действительно упустил возможность использовать в своей среде IDE (eclipse-PyDev), не касаясь командной строки и ничего не устанавливая. Итак, вот оно.

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

def count():
    from math import sqrt
    for x in range(10**5):
        sqrt(x)

if __name__ == '__main__':
    import cProfile, pstats
    cProfile.run("count()", "{}.profile".format(__file__))
    s = pstats.Stats("{}.profile".format(__file__))
    s.strip_dirs()
    s.sort_stats("time").print_stats(10)

Дополнительную информацию см. В документации или в других ответах.

person David Mašek    schedule 21.08.2015
comment
например, в профиле печатается {map} или {xxx}. как мне узнать, из какого файла вызывается метод {xxx}? мой профиль печатает {метод 'compress' объектов 'zlib.Compress'} занимает большую часть времени, но я не использую zlib, поэтому я предполагаю, что некоторые функции call numpy могут его использовать. Как узнать, какой именно файл и строка занимает много времени? - person machen; 28.10.2017
comment
Это нечестно ... Я не знаю, почему у этого отличного ответа так мало голосов ... гораздо полезнее, чем у других, получивших большое количество голосов: / - person BPL; 16.07.2020

Решение только для терминала (и простейшее) на случай, если все эти причудливые пользовательские интерфейсы не могут быть установлены или запущены:
полностью игнорирует cProfile и заменяет его на pyinstrument, который будет собирать и отображать дерево вызовов сразу после выполнения.

Установить:

$ pip install pyinstrument

Профиль и результат отображения:

$ python -m pyinstrument ./prog.py

Работает с python2 и 3.

[РЕДАКТИРОВАТЬ] Документацию API для профилирования только части кода можно найти здесь.

person Francois    schedule 07.08.2019

Следуя ответу Джо Шоу о том, что многопоточный код не работает должным образом, я решил, что метод runcall в cProfile просто выполняет вызовы self.enable() и self.disable() вокруг вызова профилированной функции, поэтому вы можете просто сделать это самостоятельно и иметь любой код, который хотите в - между ними с минимальным вмешательством в существующий код.

person PypeBros    schedule 09.11.2011
comment
Отличный совет! Быстрый взгляд на исходный код cprofile.py показывает, что именно делает runcall(). Если быть более конкретным, после создания экземпляра профиля с prof = cprofile.Profile() немедленно вызовите prof.disable(), а затем просто добавьте вызовы prof.enable() и prof.disable() вокруг раздела кода, который вы хотите профилировать. - person martineau; 22.10.2012
comment
Это очень полезно, но кажется, что код, который на самом деле находится между включением и отключением, не профилирован - только те функции, которые он вызывает. Имею ли я это право? Мне пришлось бы обернуть этот код в вызов функции, чтобы он учитывал любое из чисел в print_stats (). - person Bob Stein; 09.05.2017

В источнике Virtaal есть очень полезный класс и декоратор, который может сделать профилирование (даже для определенных методов / функций) очень простым. После этого результат можно будет очень удобно просматривать в KCacheGrind.

person Walter    schedule 24.02.2009
comment
Спасибо за этот драгоценный камень. К вашему сведению: его можно использовать как отдельный модуль с любым кодом, база кода Virtaal не требуется. Просто сохраните файл в profiling.py и импортируйте profile_func (). Используйте @profile_func () в качестве декоратора для любой функции, которую вам нужно профилировать и альт. :) - person Amjith; 06.10.2011

Если вы хотите создать кумулятивный профилировщик, то есть запускать функцию несколько раз подряд и смотреть сумму результатов.

вы можете использовать этот cumulative_profiler декоратор:

он специфичен для python> = 3.6, но вы можете удалить nonlocal, чтобы он работал в более старых версиях.

import cProfile, pstats

class _ProfileFunc:
    def __init__(self, func, sort_stats_by):
        self.func =  func
        self.profile_runs = []
        self.sort_stats_by = sort_stats_by

    def __call__(self, *args, **kwargs):
        pr = cProfile.Profile()
        pr.enable()  # this is the profiling section
        retval = self.func(*args, **kwargs)
        pr.disable()

        self.profile_runs.append(pr)
        ps = pstats.Stats(*self.profile_runs).sort_stats(self.sort_stats_by)
        return retval, ps

def cumulative_profiler(amount_of_times, sort_stats_by='time'):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            nonlocal function, amount_of_times, sort_stats_by  # for python 2.x remove this row

            profiled_func = _ProfileFunc(function, sort_stats_by)
            for i in range(amount_of_times):
                retval, ps = profiled_func(*args, **kwargs)
            ps.print_stats()
            return retval  # returns the results of the function
        return wrapper

    if callable(amount_of_times):  # incase you don't want to specify the amount of times
        func = amount_of_times  # amount_of_times is the function in here
        amount_of_times = 5  # the default amount
        return real_decorator(func)
    return real_decorator

Пример

профилирование функции baz

import time

@cumulative_profiler
def baz():
    time.sleep(1)
    time.sleep(2)
    return 1

baz()

baz прогнал 5 раз и напечатал следующее:

         20 function calls in 15.003 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       10   15.003    1.500   15.003    1.500 {built-in method time.sleep}
        5    0.000    0.000   15.003    3.001 <ipython-input-9-c89afe010372>:3(baz)
        5    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

указание количества раз

@cumulative_profiler(3)
def baz():
    ...
person moshevi    schedule 11.09.2019

cProfile отлично подходит для быстрого профилирования, но большую часть времени он заканчивался для меня ошибками. Функция runctx решает эту проблему, правильно инициализируя среду и переменные, надеюсь, это может быть кому-то полезно:

import cProfile
cProfile.runctx('foo()', None, locals())
person Datageek    schedule 30.03.2015

Мой способ - использовать yappi (https://github.com/sumerc/yappi). Это особенно полезно в сочетании с сервером RPC, где (даже только для отладки) вы регистрируете метод для запуска, остановки и печати информации о профилировании, например в этом случае:

@staticmethod
def startProfiler():
    yappi.start()

@staticmethod
def stopProfiler():
    yappi.stop()

@staticmethod
def printProfiler():
    stats = yappi.get_stats(yappi.SORTTYPE_TTOT, yappi.SORTORDER_DESC, 20)
    statPrint = '\n'
    namesArr = [len(str(stat[0])) for stat in stats.func_stats]
    log.debug("namesArr %s", str(namesArr))
    maxNameLen = max(namesArr)
    log.debug("maxNameLen: %s", maxNameLen)

    for stat in stats.func_stats:
        nameAppendSpaces = [' ' for i in range(maxNameLen - len(stat[0]))]
        log.debug('nameAppendSpaces: %s', nameAppendSpaces)
        blankSpace = ''
        for space in nameAppendSpaces:
            blankSpace += space

        log.debug("adding spaces: %s", len(nameAppendSpaces))
        statPrint = statPrint + str(stat[0]) + blankSpace + " " + str(stat[1]).ljust(8) + "\t" + str(
            round(stat[2], 2)).ljust(8 - len(str(stat[2]))) + "\t" + str(round(stat[3], 2)) + "\n"

    log.log(1000, "\nname" + ''.ljust(maxNameLen - 4) + " ncall \tttot \ttsub")
    log.log(1000, statPrint)

Затем, когда ваша программа заработает, вы можете запустить профилировщик в любое время, вызвав метод startProfiler RPC и выгрузить информацию профилирования в файл журнала, вызвав printProfiler (или изменить метод rpc, чтобы вернуть его вызывающей стороне) и получить такой вывод:

2014-02-19 16:32:24,128-|SVR-MAIN  |-(Thread-3   )-Level 1000: 
name                                                                                                                                      ncall     ttot    tsub
2014-02-19 16:32:24,128-|SVR-MAIN  |-(Thread-3   )-Level 1000: 
C:\Python27\lib\sched.py.run:80                                                                                                           22        0.11    0.05
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\xmlRpc.py.iterFnc:293                                                22        0.11    0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\serverMain.py.makeIteration:515                                                    22        0.11    0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\PicklingXMLRPC.py._dispatch:66                                       1         0.0     0.0
C:\Python27\lib\BaseHTTPServer.py.date_time_string:464                                                                                    1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py._get_raw_meminfo:243     4         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.decode_request_content:537                                                                          1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py.get_system_cpu_times:148 4         0.0     0.0
<string>.__new__:8                                                                                                                        220       0.0     0.0
C:\Python27\lib\socket.py.close:276                                                                                                       4         0.0     0.0
C:\Python27\lib\threading.py.__init__:558                                                                                                 1         0.0     0.0
<string>.__new__:8                                                                                                                        4         0.0     0.0
C:\Python27\lib\threading.py.notify:372                                                                                                   1         0.0     0.0
C:\Python27\lib\rfc822.py.getheader:285                                                                                                   4         0.0     0.0
C:\Python27\lib\BaseHTTPServer.py.handle_one_request:301                                                                                  1         0.0     0.0
C:\Python27\lib\xmlrpclib.py.end:816                                                                                                      3         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.do_POST:467                                                                                         1         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.is_rpc_path_valid:460                                                                               1         0.0     0.0
C:\Python27\lib\SocketServer.py.close_request:475                                                                                         1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\__init__.py.cpu_times:1066               4         0.0     0.0 

Это может быть не очень полезно для коротких сценариев, но помогает оптимизировать процессы серверного типа, особенно учитывая, что метод printProfiler может вызываться несколько раз с течением времени для профилирования и сравнения, например. различные сценарии использования программы.

В более новых версиях yappi будет работать следующий код:

@staticmethod
def printProfile():
    yappi.get_func_stats().print_all()
person Mr. Girgitt    schedule 19.02.2014

gprof2dot_magic

Волшебная функция для gprof2dot для профилирования любого оператора Python в виде DOT-графика в JupyterLab или Jupyter Notebook.

введите описание изображения здесь

Репозиторий GitHub: https://github.com/mattijn/gprof2dot_magic

установка

Убедитесь, что у вас есть пакет Python gprof2dot_magic.

pip install gprof2dot_magic

Его зависимости gprof2dot и graphviz также будут установлены.

использование

Чтобы включить волшебную функцию, сначала загрузите модуль gprof2dot_magic

%load_ext gprof2dot_magic

а затем профилируйте любой линейный оператор как DOT-график как таковой:

%gprof2dot print('hello world')

введите описание изображения здесь

person Mattijn    schedule 19.07.2019

Для быстрого получения статистики профиля на ноутбуке IPython. Можно встроить line_profiler и memory_profiler прямо в свои записные книжки.

Еще один полезный пакет - Pympler. Это мощный пакет профилирования, способный отслеживать классы, объекты, функции, утечки памяти и т. Д. Примеры ниже, документы прилагаются.

Возьми!

!pip install line_profiler
!pip install memory_profiler
!pip install pympler

Загрузите это!

%load_ext line_profiler
%load_ext memory_profiler

Используй это!


%время

%time print('Outputs CPU time,Wall Clock time') 
#CPU times: user 2 µs, sys: 0 ns, total: 2 µs Wall time: 5.96 µs

Дает:

  • Время ЦП: время выполнения на уровне ЦП
  • sys times: время выполнения на системном уровне
  • итого: время процессора + системное время
  • Время на стене: Время настенных часов

% timeit

%timeit -r 7 -n 1000 print('Outputs execution time of the snippet') 
#1000 loops, best of 7: 7.46 ns per loop
  • Выдает лучшее время из заданного количества прогонов (r) в цикле (n) раз.
  • Outputs details on system caching:
    • When code snippets are executed multiple times, system caches a few opearations and doesn't execute them again that may hamper the accuracy of the profile reports.

% обрезка

%prun -s cumulative 'Code to profile' 

Дает:

  • количество вызовов функций (ncalls)
  • имеет записи на вызов функции (отдельные)
  • время, затраченное на звонок (percall)
  • время, прошедшее до вызова этой функции (cumtime)
  • имя вызываемой функции / модуля и т. д.

Совокупный профиль


% memit

%memit 'Code to profile'
#peak memory: 199.45 MiB, increment: 0.00 MiB

Дает:

  • Использование памяти

% lprun

#Example function
def fun():
  for i in range(10):
    print(i)

#Usage: %lprun <name_of_the_function> function
%lprun -f fun fun()

Дает:

  • Линейная статистика

LineProfile


sys.getsizeof

sys.getsizeof('code to profile')
# 64 bytes

Возвращает размер объекта в байтах.


asizeof () от pympler

from pympler import asizeof
obj = [1,2,("hey","ha"),3]
print(asizeof.asizeof(obj,stats=4))

pympler.asizeof можно использовать для исследования того, сколько памяти потребляют определенные объекты Python. В отличие от sys.getsizeof, asizeof рекурсивно изменяет размер объектов.

pympler.asizeof


трекер от pympler

from pympler import tracker
tr = tracker.SummaryTracker()
def fun():
  li = [1,2,3]
  di = {"ha":"haha","duh":"Umm"}
fun()
tr.print_diff()

Отслеживает время жизни функции.

вывод трекера

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

Pympler doc

person Aditya Patnaik    schedule 25.05.2020

Чтобы добавить в https://stackoverflow.com/a/582337/1070617,

Я написал этот модуль, который позволяет вам использовать cProfile и легко просматривать его результаты. Подробнее здесь: https://github.com/ymichael/cprofilev

$ python -m cprofilev /your/python/program
# Go to http://localhost:4000 to view collected statistics.

См. Также: http://ymichael.com/2014/03/08/profiling-python-with-cprofile.html о том, как разобраться в собранной статистике.

person michael    schedule 21.03.2015

Новый инструмент для обработки профилей в Python - PyVmMonitor: http://www.pyvmmonitor.com/

Он имеет некоторые уникальные особенности, такие как

  • Прикрепите профилировщик к работающей (CPython) программе
  • Профилирование по запросу с интеграцией Yappi
  • Профиль на другой машине
  • Поддержка нескольких процессов (многопроцессорность, django ...)
  • Живая выборка / просмотр процессора (с выбором временного диапазона)
  • Детерминированное профилирование через интеграцию cProfile / profile
  • Анализировать существующие результаты PStats
  • Открыть файлы DOT
  • Программный доступ к API
  • Сгруппируйте образцы по методам или линиям
  • Интеграция PyDev
  • Интеграция PyCharm

Примечание: это коммерческий, но бесплатный для открытого кода.

person Fabio Zadrozny    schedule 28.04.2015

Это будет зависеть от того, что вы хотите увидеть в профилировании. Простые метрики времени можно получить с помощью (bash).

time python python_prog.py

Даже '/ usr / bin / time' может выводить подробные метрики с помощью флага '--verbose'.

Чтобы проверить показатели времени, предоставляемые каждой функцией, и лучше понять, сколько времени тратится на функции, вы можете использовать встроенный cProfile в python.

Если говорить о более подробных показателях, таких как производительность, время - не единственный показатель. Вы можете беспокоиться о памяти, потоках и т. Д.
Параметры профилирования:
1. line_profiler - еще один профилировщик, который обычно используется для построчного определения временных показателей.
2. < strong> memory_profiler - это инструмент для профилирования использования памяти.
3. heapy (из проекта Guppy) Определите, как используются объекты в куче.

Вот некоторые из самых распространенных, которые я использую. Но если вы хотите узнать больше, попробуйте прочитать эту книгу. Это очень хорошая Книга о том, как начинать с высокой производительности. Вы можете перейти к более сложным темам по использованию Cython и JIT (JIT) скомпилированного python.

person Community    schedule 19.04.2017

Я только что разработал собственный профилировщик, вдохновленный pypref_time:

https://github.com/modaresimr/auto_profiler

Добавив декоратор, он покажет дерево трудоемких функций.

@Profiler(depth=4, on_disable=show)

Install by: pip install auto_profiler

Пример

import time # line number 1
import random

from auto_profiler import Profiler, Tree

def f1():
    mysleep(.6+random.random())

def mysleep(t):
    time.sleep(t)

def fact(i):
    f1()
    if(i==1):
        return 1
    return i*fact(i-1)


def show(p):
    print('Time   [Hits * PerHit] Function name [Called from] [Function Location]\n'+\
          '-----------------------------------------------------------------------')
    print(Tree(p.root, threshold=0.5))
    
@Profiler(depth=4, on_disable=show)
def main():
    for i in range(5):
        f1()

    fact(3)


if __name__ == '__main__':
    main()

Пример вывода


Time   [Hits * PerHit] Function name [Called from] [function location]
-----------------------------------------------------------------------
8.974s [1 * 8.974]  main  [auto-profiler/profiler.py:267]  [/test/t2.py:30]
├── 5.954s [5 * 1.191]  f1  [/test/t2.py:34]  [/test/t2.py:14]
│   └── 5.954s [5 * 1.191]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
│       └── 5.954s [5 * 1.191]  <time.sleep>
|
|
|   # The rest is for the example recursive function call fact
└── 3.020s [1 * 3.020]  fact  [/test/t2.py:36]  [/test/t2.py:20]
    ├── 0.849s [1 * 0.849]  f1  [/test/t2.py:21]  [/test/t2.py:14]
    │   └── 0.849s [1 * 0.849]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
    │       └── 0.849s [1 * 0.849]  <time.sleep>
    └── 2.171s [1 * 2.171]  fact  [/test/t2.py:24]  [/test/t2.py:20]
        ├── 1.552s [1 * 1.552]  f1  [/test/t2.py:21]  [/test/t2.py:14]
        │   └── 1.552s [1 * 1.552]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
        └── 0.619s [1 * 0.619]  fact  [/test/t2.py:24]  [/test/t2.py:20]
            └── 0.619s [1 * 0.619]  f1  [/test/t2.py:21]  [/test/t2.py:14]
person Ali    schedule 12.02.2020

С помощью статистического профилировщика, такого как austin, никаких инструментов не требуется, что означает, что вы можете получать данные профилирования из Приложение Python просто с

austin python3 my_script.py

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

austin python3 my_script.py | flamegraph.pl > my_script_profile.svg

Кроме того, вы также можете использовать веб-приложение Speedscope.app для быстрой визуализации собранных образцов. Если у вас установлен pprof, вы также можете получить austin-python (например, pipx install austin-python) и используйте austin2pprof для скрытия в формате pprof.

person Phoenix87    schedule 13.03.2020

Вы когда-нибудь хотели знать, что, черт возьми, делает этот скрипт Python? Войдите в Inspect Shell. Inspect Shell позволяет печатать / изменять глобальные переменные и запускать функции, не прерывая выполняющийся скрипт. Теперь с автозаполнением и историей команд (только в Linux).

Inspect Shell - это не отладчик в стиле pdb.

https://github.com/amoffat/Inspect-Shell

Вы можете использовать это (и свои наручные часы).

person Colonel Panic    schedule 13.10.2012

Недавно я создал плагин для PyCharm, с помощью которого вы можете легко анализировать и визуализировать результаты line_profiler в редакторе PyCharm.

line_profiler также упоминался в других ответах и ​​является отличным инструментом для точного анализа того, сколько времени тратится интерпретатором python в определенных строках.

Плагин PyCharm, который я создал, можно найти здесь: https://plugins.jetbrains.com/plugin/16536-line-profiler

Ему нужен вспомогательный пакет в вашей среде Python под названием line-profiler-pycharm, который может быть установлен с помощью pip или самим плагином.

После установки плагина в PyCharm:

  1. Украсьте любую функцию, которую хотите профилировать, с помощью декоратора line_profiler_pycharm.profile
  2. Бегите с бегуном 'Profile Lines'

Снимок экрана с результатами:  Результаты Pycharm профилировщика линий

person jusx    schedule 14.06.2021
comment
Довольно аккуратно, легко реализуется! - person Marc; 30.06.2021

Также существует статистический профилировщик под названием statprof. Это профилировщик выборки, поэтому он добавляет минимальные накладные расходы к вашему коду и дает тайминги на основе строк (а не только функций). Он больше подходит для программных приложений реального времени, таких как игры, но может иметь меньшую точность, чем cProfile.

Версия в pypi немного устарела, поэтому ее можно установить с pip, указав репозиторий git:

pip install git+git://github.com/bos/statprof.py@1a33eba91899afe17a8b752c6dfdec6f05dd0c01

Вы можете запустить его так:

import statprof

with statprof.profile():
    my_questionable_function()

См. Также https://stackoverflow.com/a/10333592/320036

person z0r    schedule 11.02.2016

Когда у меня нет root-прав на сервере, я использую lsprofcalltree.py и запускаю моя программа такая:

python lsprofcalltree.py -o callgrind.1 test.py

Затем я могу открыть отчет с помощью любого программного обеспечения, совместимого с callgrind, например qcachegrind

person Vincent Fenet    schedule 02.02.2017