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

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

1452
Chris Lawlor 24 Фев 2009 в 19:01

26 ответов

Лучший ответ

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.

1325
albert 26 Июн 2018 в 02:56

Некоторое время назад я создал 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

415
Graham 7 Янв 2019 в 05:02

Я только что разработал свой собственный профилировщик, вдохновленный 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]
0
Ali 12 Фев 2020 в 12:03

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

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

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

enter image description here

73
zaxliu 25 Май 2016 в 08:06

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

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

33
Ian Langmore 20 Окт 2011 в 16:05

Pprofile

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

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

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

Vprof

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

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

heatmap

32
BenC 22 Май 2016 в 22:48

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

Осмотрите Shell не отладчик в стиле pdb.

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

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

3
Colonel Panic 22 Окт 2012 в 14:52

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

Я действительно упустил какой-то способ, которым я мог бы использовать в своей 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)

См. документы или другие ответы для получения дополнительной информации.

13
David Mašek 21 Авг 2015 в 11:59

Мой способ - использовать 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)

Затем, когда ваша программа заработает, вы можете в любой момент запустить профилировщик, вызвав метод RPC startProfiler и выгрузив информацию о профилировании в файл журнала, вызвав 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()
6
Jonno_FTW 29 Янв 2020 в 00:55

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

У этого есть некоторые уникальные особенности, такие как

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

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

4
Fabio Zadrozny 28 Апр 2015 в 22:50

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

10
Walter 30 Окт 2017 в 17:28

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

enter image description here

Установить с

pip3 install tuna

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

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

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

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

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

tuna program.prof
29
noamgot 16 Дек 2019 в 08:08

Я думаю, что 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

Результат:

Screenshot of the result

53
Federico 16 Ноя 2017 в 15:11

@ Комментарий Макси в отношении этого ответа выручил меня настолько, что я думаю, что он заслуживает своего собственного ответа: у меня уже был сгенерированный 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 example output

132
Community 23 Май 2017 в 12:26

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

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

194
Joe Shaw 15 Мар 2014 в 17:01

Чтобы добавить к 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 о том, как разобраться в собранной статистике.

3
Community 23 Май 2017 в 11:33

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

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

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

0
Vincent Fenet 2 Фев 2017 в 10:18

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

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

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

enter image description here

41
Pete 25 Фев 2015 в 17:58

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

time python python_prog.py

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

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

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

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

3
Vadim Kotov 7 Ноя 2017 в 15:42

< Сильный > 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

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

< Сильный > Использование

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

%load_ext gprof2dot_magic

А затем профилировать любой оператор строки как график DOT как таковой:

%gprof2dot print('hello world')

enter image description here

4
Mattijn 19 Июл 2019 в 14:25

Также есть статистический профилировщик 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

1
Community 23 Май 2017 в 11:47

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

Как это документы по питону: 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-профиль

apt-get install python-profiler 

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

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

 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 (который предоставляет программу dot):

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
143
Dewamuval 16 Фев 2020 в 20:12

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

1. pip install snakeviz

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

3. snakeviz temp.dat

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

58
CodeCabbie 8 Янв 2020 в 09:17

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

12
PypeBros 9 Ноя 2011 в 12:59

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

Установка:

$ pip install pyinstrument

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

$ python -m pyinstrument ./prog.py

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

6
Francois 10 Дек 2019 в 12:53

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

import cProfile
cProfile.runctx('foo()', None, locals())
9
Datageek 30 Мар 2015 в 11:11