Python 코드를 한 줄씩 프로파일 링하려면 어떻게해야합니까?


116

나는 cProfile을 사용하여 내 코드를 프로파일 링했으며 훌륭하게 작동하고 있습니다. 또한 결과를 시각화하기 위해 gprof2dot.py 를 사용합니다 (조금 더 명확 해짐 ).

그러나 cProfile (및 지금까지 본 대부분의 다른 Python 프로파일 러)은 함수 호출 수준에서만 프로파일 링하는 것 같습니다. 이로 인해 특정 함수가 다른 위치에서 호출 될 때 혼란이 발생합니다. 호출 # 1 또는 호출 # 2가 대부분의 시간을 차지하는지 알 수 없습니다. 문제의 함수가 다른 7 곳에서 호출 된 6 단계 깊이 일 때 이것은 더욱 악화됩니다.

라인 별 프로파일 링은 어떻게 얻습니까?

대신 :

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은 부모에게 "전송"되는 총 시간을 보여 주지만 여러 계층과 상호 연결된 호출이있을 때 다시이 연결이 끊어집니다.

이상적으로는 데이터를 구문 분석 한 다음 각 줄에 주어진 총 시간과 함께 내 소스 파일을 표시하는 GUI를 갖고 싶습니다. 이 같은:

main.py:

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

그런 다음 두 번째 "func (c)"호출을 클릭하여 "func (a)"호출과 별도로 해당 호출에서 시간을 차지하는 것을 확인할 수 있습니다.

말이 돼? 이러한 유형의 정보를 수집하는 프로파일 링 라이브러리가 있습니까? 내가 놓친 멋진 도구가 있습니까?


2
내 생각에는 당신이 관심을 가질 것입니다 pstats.print_callers. 여기 에 예가 있습니다 .
Muhammad Alkarouri

무함마드, 확실히 도움이 되네요! 최소한 원점에 따라 함수 호출을 분리하는 한 가지 문제를 해결합니다. Joe Kington의 대답이 내 목표에 더 가깝다고 생각하지만 print_callers ()는 확실히 저를 반쯤 얻습니다. 감사!
rocketmonkeys

답변:


120

나는 그것이 Robert Kern의 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

도움이 되었기를 바랍니다.


10
line_profiler는 Python 3에서 작동합니까? 그것에 대한 정보를 얻을 수 없었습니다.
user1251007 jul.

3
line_profiler는 나를 위해 히트와 시간을 표시하지 않습니다. 아무도 이유를 말해 줄 수 있습니까? 그리고 해결하는 방법?
I159

6
제가 작성한 데코레이터는 다음과 같습니다 : gist.github.com/kylegibson/6583590 . nosetest를 실행하는 경우 stdout이 즉시 인쇄되도록 -s 옵션을 사용해야합니다.
Kyle Gibson

5
이 출력을 생성하는 파이썬 스크립트는 어떻게 생겼습니까? import line_profiler;그리고 ?
Zhubarb

10
누구든지이 라이브러리를 실제로 사용하는 방법을 보여줄 수 있습니까? 추가 정보는 설치 방법을 가르치고, 다양한 자주 묻는 질문 (FAQ)과 답변,하지만 핍은 .. 설치 한 후 그것을 사용하는 방법을 언급하지 않습니다
cryanbhu

47

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 를 사용 하여 큰 결과를 쉽게 찾아 볼 수 있습니다.

공개 : 저는 pprofile 작성자입니다.


1
+1 기여해 주셔서 감사합니다. 잘 된 것 같습니다. 저는 약간 다른 관점을 가지고 있습니다. 진술과 기능에 소요되는 포괄적 인 시간을 측정하는 것이 하나의 목표입니다. 코드를 더 빠르게 만들기 위해 수행 할 수있는 작업을 찾는 것은 다른 목표입니다. 10 ^ 6 줄의 코드처럼 코드가 커질수록 차이가 분명해집니다. 코드는 많은 시간을 낭비 할 수 있습니다. 내가 찾은 방법은 매우 상세한 샘플을 몇 개 취해 요약이 아닌 육안으로 검사하는 것입니다. 문제는 시간 낭비로 드러납니다.
Mike Dunlavey 2015

1
당신 말이 맞습니다. 저는 더 작은 서브 세트를 프로파일 링하고 싶을 때 pprofile 사용법을 언급하지 않았습니다. 이에 대한 예를 추가하기 위해 게시물을 편집했습니다.
vpelletier 2015

3
이것이 바로 제가 찾던 것입니다 : 방해가되지 않고 광범위합니다.
egpbos 2016 년

1
좋은 도구이지만 원래 코드보다 몇 배 느리게 실행됩니다.
rominf

4

이를 위해 line_profiler 패키지의 도움을받을 수 있습니다.

1. 먼저 패키지를 설치합니다.

    pip install line_profiler

2. 매직 명령을 사용하여 파이썬 / 노트북 환경에 패키지를로드합니다.

    %load_ext line_profiler

3. 기능에 대한 코드를 프로파일 링하려면 다음을
수행하십시오.

    %lprun -f demo_func demo_func(arg1, arg2)

다음 단계를 수행하면 모든 세부 정보가 포함 된 멋진 형식의 출력을 얻을 수 있습니다.

Line #      Hits      Time    Per Hit   % Time  Line Contents
 1                                           def demo_func(a,b):
 2         1        248.0    248.0     64.8      print(a+b)
 3         1         40.0     40.0     10.4      print(a)
 4         1         94.0     94.0     24.5      print(a*b)
 5         1          1.0      1.0      0.3      return a/b

4

@Joe Kington의 위에서 언급 한 답변 을 개선하기 위해 .

대한 파이썬 3.x를 , 사용 line_profiler :


설치:

pip install line_profiler

용법:

프로그램 main.py과 그 안에 기능이 fun_a()있고 fun_b()시간과 관련하여 프로파일 링하고 싶다고 가정합니다 . @profile함수 정의 바로 전에 데코레이터를 사용해야합니다 . 예를 들어,

@profile
def fun_a():
    #do something

@profile
def fun_b():
    #do something more

if __name__ == '__main__':
    fun_a()
    fun_b()

프로그램은 쉘 명령을 실행하여 프로파일 링 할 수 있습니다.

$ kernprof -l -v main.py

인수는 다음을 사용하여 가져올 수 있습니다. $ kernprof -h

Usage: kernprof [-s setupfile] [-o output_file_path] scriptfile [arg] ...

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -l, --line-by-line    Use the line-by-line profiler from the line_profiler
                        module instead of Profile. Implies --builtin.
  -b, --builtin         Put 'profile' in the builtins. Use 'profile.enable()'
                        and 'profile.disable()' in your code to turn it on and
                        off, or '@profile' to decorate a single function, or
                        'with profile:' to profile a single section of code.
  -o OUTFILE, --outfile=OUTFILE
                        Save stats to <outfile>
  -s SETUP, --setup=SETUP
                        Code to execute before the code to profile
  -v, --view            View the results of the profile in addition to saving
                        it.

결과는 다음과 같이 콘솔에 인쇄됩니다.

Total time: 17.6699 s
File: main.py
Function: fun_a at line 5

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    5                                           @profile
    6                                           def fun_a():
...


편집 : 프로파일 러의 결과는 TAMPPA 패키지를 사용하여 구문 분석 할 수 있습니다 . 이를 사용하여 원하는 줄 단위 플롯을 다음과 같이 얻을 수 있습니다. 음모


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.