Как вы можете профилировать скрипт 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 подобный изображению ниже:

enter image description here

Ответ 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 теряет меньше информации, хотя:

gprof2dot example output

Ответ 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 сказал "профилирование", но, похоже, он имел в виду "время". Имейте в виду, что программы будут работать медленнее при профилировании.

enter image description here

Ответ 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, таких как время работы и использование памяти.

heatmap

Ответ 12

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

enter image description here

Установить с

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.

enter image description here

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')

enter image description here

Ответ 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.