Подтвердить что ты не робот

Как я могу профилировать код Python построчно?

Я использовал cProfile для профилирования своего кода, и он работал отлично. Я также использую gprof2dot.py для визуализации результатов (делает это немного яснее).

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

Как мне получить построчное профилирование?

Вместо этого:

function #12, total time: 2.0s

Я хотел бы видеть что-то вроде этого:

function #12 (called from somefile.py:102) 0.5s
function #12 (called from main.py:12) 1.5s

cProfile показывает, сколько всего времени "переходит" родителю, но опять-таки это соединение теряется, когда у вас есть несколько слоев и взаимосвязанные вызовы.

В идеале я хотел бы иметь графический интерфейс, который будет анализировать данные, а затем показывать мне мой исходный файл с общим временем, указанным для каждой строки. Что-то вроде этого:

main.py:

a = 1 # 0.0s
result = func(a) # 0.4s
c = 1000 # 0.0s
result = func(c) # 5.0s

Тогда я смогу нажать на второй вызов "func (c)", чтобы увидеть, что занимает время в этом вызове, отдельно от вызова "func (a)".

Имеет ли это смысл? Есть ли библиотека профилирования, которая собирает информацию такого типа? Есть ли какой-то потрясающий инструмент, который я пропустил?

4b9b3361

Ответ 1

Я верю в то, для чего предназначен Роберт Керн line_profiler. По ссылке:

File: pystone.py
Function: Proc2 at line 149
Total time: 0.606656 s

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   149                                           @profile
   150                                           def Proc2(IntParIO):
   151     50000        82003      1.6     13.5      IntLoc = IntParIO + 10
   152     50000        63162      1.3     10.4      while 1:
   153     50000        69065      1.4     11.4          if Char1Glob == 'A':
   154     50000        66354      1.3     10.9              IntLoc = IntLoc - 1
   155     50000        67263      1.3     11.1              IntParIO = IntLoc - IntGlob
   156     50000        65494      1.3     10.8              EnumLoc = Ident1
   157     50000        68001      1.4     11.2          if EnumLoc == Ident1:
   158     50000        63739      1.3     10.5              break
   159     50000        61575      1.2     10.1      return IntParIO

Надеюсь, это поможет!

Ответ 2

Вы также можете использовать pprofile(pypi). Если вы хотите профилировать все выполнение, это не требует модификации исходного кода. Вы также можете профилировать подмножество более крупной программы двумя способами:

  • переключать профилирование при достижении определенной точки в коде, например:

    import pprofile
    profiler = pprofile.Profile()
    with profiler:
        some_code
    # Process profile content: generate a cachegrind file and send it to user.
    
    # You can also write the result to the console:
    profiler.print_stats()
    
    # Or to a file:
    profiler.dump_stats("/tmp/profiler_stats.txt")
    
  • асинхронно переключать профилирование из стека вызовов (требуется способ запуска этого кода в рассматриваемом приложении, например, обработчике сигнала или доступном рабочем потоке) с использованием статистического профилирования:

    import pprofile
    profiler = pprofile.StatisticalProfile()
    statistical_profiler_thread = pprofile.StatisticalThread(
        profiler=profiler,
    )
    with statistical_profiler_thread:
        sleep(n)
    # Likewise, process profile content
    

Формат вывода аннотации кода очень похож на профилировщик строки:

$ pprofile --threads 0 demo/threads.py
Command line: ['demo/threads.py']
Total duration: 1.00573s
File: demo/threads.py
File duration: 1.00168s (99.60%)
Line #|      Hits|         Time| Time per hit|      %|Source code
------+----------+-------------+-------------+-------+-----------
     1|         2|  3.21865e-05|  1.60933e-05|  0.00%|import threading
     2|         1|  5.96046e-06|  5.96046e-06|  0.00%|import time
     3|         0|            0|            0|  0.00%|
     4|         2|   1.5974e-05|  7.98702e-06|  0.00%|def func():
     5|         1|      1.00111|      1.00111| 99.54%|  time.sleep(1)
     6|         0|            0|            0|  0.00%|
     7|         2|  2.00272e-05|  1.00136e-05|  0.00%|def func2():
     8|         1|  1.69277e-05|  1.69277e-05|  0.00%|  pass
     9|         0|            0|            0|  0.00%|
    10|         1|  1.81198e-05|  1.81198e-05|  0.00%|t1 = threading.Thread(target=func)
(call)|         1|  0.000610828|  0.000610828|  0.06%|# /usr/lib/python2.7/threading.py:436 __init__
    11|         1|  1.52588e-05|  1.52588e-05|  0.00%|t2 = threading.Thread(target=func)
(call)|         1|  0.000438929|  0.000438929|  0.04%|# /usr/lib/python2.7/threading.py:436 __init__
    12|         1|  4.79221e-05|  4.79221e-05|  0.00%|t1.start()
(call)|         1|  0.000843048|  0.000843048|  0.08%|# /usr/lib/python2.7/threading.py:485 start
    13|         1|  6.48499e-05|  6.48499e-05|  0.01%|t2.start()
(call)|         1|   0.00115609|   0.00115609|  0.11%|# /usr/lib/python2.7/threading.py:485 start
    14|         1|  0.000205994|  0.000205994|  0.02%|(func(), func2())
(call)|         1|      1.00112|      1.00112| 99.54%|# demo/threads.py:4 func
(call)|         1|  3.09944e-05|  3.09944e-05|  0.00%|# demo/threads.py:7 func2
    15|         1|  7.62939e-05|  7.62939e-05|  0.01%|t1.join()
(call)|         1|  0.000423908|  0.000423908|  0.04%|# /usr/lib/python2.7/threading.py:653 join
    16|         1|  5.26905e-05|  5.26905e-05|  0.01%|t2.join()
(call)|         1|  0.000320196|  0.000320196|  0.03%|# /usr/lib/python2.7/threading.py:653 join

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

Он может генерировать выходные данные в формате cachegrind, поэтому вы можете использовать kcachegrind для удобного просмотра больших результатов.

Раскрытие информации: я являюсь автором профиля.

Ответ 3

PyVmMonitor имеет живой просмотр, который может помочь вам там (вы можете подключиться к запущенной программе и получить от нее статистику).

Смотрите: http://www.pyvmmonitor.com/

Ответ 4

Вы можете воспользоваться помощью пакета line_profiler для этого

1. Сначала установите пакет:

    pip install line_profiler

2. Используйте волшебную команду для загрузки пакета в вашу среду Python/Notebook.

    %load_ext line_profiler

3. Если вы хотите профилировать коды для функции, то
сделать следующее: % lprun -f имя_функции имя_функции

    %lprun -f function_defined_by_you function_defined_by_you(arg1, arg2)

ВЫ ПОЛУЧИТЕ ОТЛИЧНЫЙ ФОРМАТИРОВАННЫЙ ВЫХОД СО ВСЕМИ ДЕТАЛЯМИ, ЕСЛИ ВЫ СЛЕДУЕТЕ ВЫШЕПРИ ВЫШЕ ШАГОВ