Как вы можете профилировать скрипт Python?
Project Euler и другие соревнования по кодированию часто имеют максимальное время для запуска, или люди хвастаются тем, как быстро выполняется их конкретное решение. С python иногда подходы несколько kludgey - то есть добавление кода времени в __main__
.
Каков хороший способ определить, как долго запускается программа python для запуска?
Ответы
Ответ 1
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 Profiling
Также через YouTube.
Ответ 2
Некоторое время назад я сделал 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
подобный изображению ниже:
Ответ 3
Стоит отметить, что использование профилировщика работает (по умолчанию) в основном потоке, и вы не получите никакой информации из других потоков, если используете их. Это может быть немного gotcha, поскольку он полностью не упоминается в документации профилировщика.
Если вы также хотите профилировать темы, вам нужно посмотреть threading.setprofile()
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
вместо стандартного. Это может дать вам большую гибкость, но я не уверен, что это того стоит, особенно если вы используете сторонний код, который не будет использовать ваш класс.
Ответ 4
Вики python - отличная страница для профилирования ресурсов:
http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code
как и документы python:
http://docs.python.org/library/profile.html
как показано Chris Lawlor, cProfile - отличный инструмент и может быть легко использован для печати на экране:
python -m cProfile -s time mine.py <args>
или файл:
python -m cProfile -o output.file mine.py <args>
PS > Если вы используете Ubuntu, обязательно установите python-profile
sudo apt-get install python-profiler
Если вы выходите в файл, вы можете получить приятные визуализации, используя следующие инструменты
PyCallGraph: инструмент для создания изображений графиков вызовов
установить:
sudo pip install pycallgraph
пробег:
pycallgraph mine.py args
Вид:
gimp pycallgraph.png
Вы можете использовать все, что хотите, чтобы просмотреть файл png, я использовал gimp
К сожалению, я часто получаю
точка: граф слишком велик для растровых изображений каир-рендеринга. Масштабирование по 0,257079 для соответствия
что делает мои изображения необычно маленькими. Поэтому я обычно создаю файлы svg:
pycallgraph -f svg -o pycallgraph.svg mine.py <args>
PS > обязательно установите graphviz (который предоставляет программу-точка):
sudo pip install graphviz
Альтернативное отображение с использованием gprof2dot через @maxy/@quodlibetor:
sudo pip install gprof2dot
python -m cProfile -o profile.pstats mine.py
gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg
Ответ 5
Комментарий @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
и BLAM!
Он использует точку (то же самое, что использует pycallgraph), поэтому вывод выглядит аналогичным образом. У меня создается впечатление, что gprof2dot теряет меньше информации, хотя:
Ответ 6
При исследовании этой темы я столкнулся с удобным инструментом под названием SnakeViz. SnakeViz - это инструмент для визуализации на основе веб-интерфейса. Его очень легко установить и использовать. Обычно я использую его, чтобы сгенерировать файл stat с %prun
, а затем выполнить анализ в SnakeViz.
Используемый основной метод - график Sunburst, как показано ниже, в котором иерархия вызовов функций организована как слои дуг и информации времени, закодированной в их ширинах angular.
Лучше всего вы можете взаимодействовать с диаграммой. Например, чтобы увеличить масштаб, вы можете щелкнуть мышью по дуге, и дуга и ее потомки будут увеличены в виде нового солнечного света, чтобы отобразить более подробную информацию.
Ответ 7
Я думаю, что cProfile
отлично подходит для профилирования, а kcachegrind
отлично подходит для визуализации результатов. pyprof2calltree
находится между обработкой файла.
python -m cProfile -o script.profile script.py
pyprof2calltree -i script.profile -o script.calltree
kcachegrind script.calltree
Чтобы установить необходимые инструменты (по крайней мере, на Ubuntu):
apt-get install kcachegrind
pip install pyprof2calltree
Результат:
Ответ 8
Также стоит упомянуть GUI cProfile dump viewer RunSnakeRun. Он позволяет сортировать и выбирать, тем самым увеличивая масштаб в соответствующих частях программы. Размеры прямоугольников на изображении пропорциональны времени. Если вы наведите указатель мыши на прямоугольник, выделите этот вызов в таблице и всюду на карте. Когда вы дважды щелкаете по прямоугольнику, он масштабируется на этой части. Он покажет вам, кто называет эту часть и что вызывает эта часть.
Описательная информация очень полезна. Он показывает вам код для этого бита, который может быть полезен, когда вы имеете дело со встроенными вызовами библиотеки. Он сообщает вам, какой файл и какую строку найти код.
Также хочу указать, что OP сказал "профилирование", но, похоже, он имел в виду "время". Имейте в виду, что программы будут работать медленнее при профилировании.
Ответ 9
Самый простой и быстрый способ найти, куда все время идет.
1. pip install snakeviz
2. python -m cProfile -o temp.dat <PROGRAM>.py
3. snakeviz temp.dat
Рисует круговую диаграмму в браузере. Самая большая часть - это проблемная функция. Очень просто.
Ответ 10
Хорошим профилирующим модулем является line_profiler (называемый с помощью script kernprof.py). Его можно загрузить здесь.
Я понимаю, что cProfile предоставляет информацию об общем времени, затраченном на каждую функцию. Таким образом, отдельные строки кода не синхронизированы. Это проблема в научных вычислениях, поскольку часто одна линия может занимать много времени. Кроме того, как я помню, cProfile не поймал время, которое я тратил на использование numpy.dot.
Ответ 11
pprofile
line_profiler
(уже представленный здесь) также вдохновил pprofile
, который описывается как:
Линейная гранулярность, детерминированный и статистический чистый питон профилировщик
Он обеспечивает линейную детализацию как line_profiler
, является чистым Python, может использоваться как отдельная команда или модуль и может даже генерировать файлы формата callgrind, которые можно легко проанализировать с помощью [k|q]cachegrind
.
vprof
Существует также vprof, пакет Python, описанный как:
[...] предоставление богатых и интерактивных визуализаций для различных характеристик программы Python, таких как время работы и использование памяти.
Ответ 12
Недавно я создал тунца для визуализации Python во время выполнения и импорта профилей; это может быть полезно здесь.
Установить с
pip3 install tuna
Создать профиль времени выполнения
python -mcProfile -o program.prof yourfile.py
или профиль импорта (требуется Python 3. 7+)
python -X importprofile yourfile.py 2> import.log
Тогда просто запустите тунца на файл
tuna program.prof
Ответ 13
Там много замечательных ответов, но они либо используют командную строку, либо внешнюю программу для профилирования и/или сортировки результатов.
Я действительно пропустил какой-то способ, который мог бы использовать в моей среде 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)
Подробнее см. docs или другие ответы.
Ответ 14
После того, как Джо Шоу ответит, что многопоточный код не работает должным образом, я понял, что метод runcall
в cProfile просто выполняет вызовы self.enable()
и self.disable()
вокруг профилированного вызова функции, поэтому вы можете просто сделать что вы сами и имеете любой код, который вы хотите, между минимальными помехами с существующим кодом.
Ответ 15
В Virtaal источник есть очень полезный класс и декоратор, который может сделать профилирование (даже для определенных методов/функций) очень простым. Результат можно будет удобно просмотреть в KCacheGrind.
Ответ 16
cProfile отлично подходит для быстрого профилирования, но большую часть времени он заканчивался для меня ошибками. Функция runctx решает эту проблему, правильно инициализируя среду и переменные, надеясь, что она может быть полезна для кого-то:
import cProfile
cProfile.runctx('foo()', None, locals())
Ответ 17
Мой способ - использовать yappi (https://code.google.com/p/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
можно многократно называть профилем и сравнивать, например. разные сценарии использования программы.
Ответ 18
Всегда хотите знать, какого черта, что делает python script? Введите Проверьте оболочку. Inspect Shell позволяет печатать/изменять глобальные значения и запускать функции без прерывания работы script. Теперь с автозаполнение и история команд (только для Linux).
Inspect Shell не является отладчиком в стиле pdb.
https://github.com/amoffat/Inspect-Shell
Вы можете использовать это (и ваши наручные часы).
Ответ 19
Чтобы добавить к fooobar.com/questions/11372/...,
Я написал этот модуль, который позволяет вам легко использовать 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 о том, как понять собранную статистику.
Ответ 20
Новый инструмент для обработки профилирования в Python - это PyVmMonitor: http://www.pyvmmonitor.com/
Он имеет некоторые уникальные функции, такие как
- Прикрепите профилировщик к запущенной (CPython) программе
- Профилирование по требованию с интеграцией Yappi
- Профиль на другой машине
- Поддержка нескольких процессов (многопроцессорность, django...)
- Живая выборка/просмотр процессора (с выбором диапазона времени)
- Детерминированное профилирование через интеграцию cProfile/profile
- Анализ существующих результатов PStats
- Открыть файлы DOT
- Доступ к программному API.
- Групповые выборки по методу или строке
- Интеграция PyDev
- Интеграция PyCharm
Примечание: он коммерческий, но бесплатный для открытого источника.
Ответ 21
Это будет зависеть от того, что вы хотите увидеть из профилирования. Простое время
метрики могут быть заданы (bash).
time python python_prog.py
Даже "/usr/bin/time" может выводить подробные показатели с помощью флага "--verbose".
Чтобы проверить метрики времени, заданные каждой функцией, и чтобы лучше понять, сколько времени тратится на функции, вы можете использовать встроенный cProfile в python.
Переходя к более подробным показателям, таким как производительность, время не является единственным показателем. Вы можете беспокоиться о памяти, потоках и т.д.
Параметры профилирования:
1. line_profiler - это другой профайлер, обычно используемый для определения временных показателей по очереди.
2. memory_profiler - инструмент для использования памяти в профиле.
3. heapy (из проекта Guppy) Профиль, как используются объекты в куче.
Вот некоторые из распространенных я обычно использую. Но если вы хотите узнать больше, попробуйте прочитать эту книгу
Это довольно хорошая книга о том, как начать с производительности. Вы можете перейти на расширенные темы при использовании компиляции питона Cython и JIT (Just-in-time).
Ответ 22
Также существует статистический профайлер, называемый statprof
. Это профилировщик выборки, поэтому он добавляет минимальные накладные расходы на ваш код и дает линейные (а не только функциональные) тайминги. Он больше подходит для мягких приложений реального времени, таких как игры, но может иметь меньшую точность, чем cProfile.
Версия в pypi немного устарела, поэтому ее можно установить с помощью pip
, указав репозиторий git:
pip install git+git://github.com/bos/[email protected]
Вы можете запустить его следующим образом:
import statprof
with statprof.profile():
my_questionable_function()
См. также fooobar.com/questions/11395/...
Ответ 23
gprof2dot_magic
Волшебная функция для gprof2dot
для профилирования любого оператора Python как графика DOT в JupyterLab или Jupyter Notebook.
GitHub РЕПО: https://github.com/mattijn/gprof2dot_magic
монтаж
Убедитесь, что у вас есть пакет Python gprof2dot_magic
.
pip install gprof2dot_magic
graphviz
будут установлены его зависимости gprof2dot
и graphviz
использование
Чтобы включить магическую функцию, сначала загрузите модуль gprof2dot_magic
%load_ext gprof2dot_magic
а затем профилировать любой оператор строки как график DOT как таковой:
%gprof2dot print('hello world')
Ответ 24
Если вы хотите сделать накопительный профилировщик,
то есть запускать функцию несколько раз подряд и смотреть сумму результатов.
Вы можете использовать этот декоратор 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
ПримерExample
профилирование функции 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():
...
Ответ 25
Когда я не root на сервере, я использую
lsprofcalltree.py и запустите мою программу следующим образом:
python lsprofcalltree.py -o callgrind.1 test.py
Затем я могу открыть отчет с любым программным обеспечением, совместимым с callgrind, например qcachegrind
Ответ 26
Решение для терминала (и самое простое), если все эти модные интерфейсы не устанавливаются или не запускаются:
полностью игнорируйте cProfile
и замените его на pyinstrument
, который будет собирать и отображать дерево вызовов сразу после выполнения.
Установить:
$ pip install pyinstrument
Профиль и результат отображения:
$ python -m pyinstrument ./prog.py
Работает с python2 и 3.