2010-10-13 60 views
72

我一直在使用cProfile來剖析我的代碼,並且它一直在很好地工作。我還使用gprof2dot.py來顯示結果(使其更清晰一些)。如何逐行分析python代碼?

但是,cProfile(以及目前爲止我所見過的大多數其他Python配置文件)似乎只能在函數調用級別進行配置。當某些功能從不同的地方被調用時,這會引起混淆 - 我不知道1號或2號呼叫是否佔用大部分時間。當所討論的功能是6個層次時,這會變得更糟,從另外7個地方調用。

所以我的問題是:如何獲得一行一行的分析?取而代之的是:

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(三)」打電話,看看有什麼佔用時間在這一號召,從「FUNC(一)」呼叫分開。

這有道理嗎?是否有任何分析庫收集此類信息?有沒有我錯過的一些很棒的工具?任何反饋意見。謝謝!!

+2

我的猜測是,你會感興趣的'pstats.print_callers'。一個例子是[這裏](http://www.doughellmann.com/PyMOTW/profile/)。 – 2010-10-13 20:18:59

+0

穆罕默德,這絕對有幫助!至少它解決了一個問題:根據原點分離函數調用。我認爲喬金頓的答案更接近我的目標,但print_callers()肯定會讓我獲得一半。謝謝! – rocketmonkeys 2010-10-14 15:25:25

回答

84

我相信這就是Robert Kern's 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 

希望幫助!

+1

喬,這*完全是我在找的東西。我可以使用一個裝飾器,將一個LineProfiler()對象附加到一些函數中,然後它將逐個分析該函數的輪廓。我真的希望有一個圖形化的方式來看結果,但這是一個很好的開始!謝謝! – rocketmonkeys 2010-10-14 15:40:46

+1

作爲一個後續:我已經使用過這幾次了,我甚至還製作了一個django decorator @profiler來自動在這個逐行剖析器中包裝視圖,並將結果吐出。這是完美的!這是我在分析視圖時真正需要的。它不能遞歸地顯示我需要花費的時間,但我至少可以將它縮小到一行。這通常只是我需要的。再次感謝! – rocketmonkeys 2010-11-30 15:15:01

+3

line_profiler是否適用於Python 3?我無法獲得有關這方面的任何信息。 – user1251007 2012-07-23 15:02:39

19

您也可以使用pprofilepypi)。 如果你想剖析整個執行過程,它不需要修改源代碼。在代碼中達到特定點時,如

  • 撥動分析:

    import pprofile 
    profiler = pprofile.Profile() 
    with profiler: 
        some_code 
    # Process profile content: generate a cachegrind file and send it to user. 
    
  • 撥動從調用堆棧異步剖析 您也可以分析一個更大的計劃的一個子集在兩個方面(需要通過使用統計分析來在所考慮的應用中觸發此代碼的方式,例如信號處理程序或可用的工作線程):

    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的作者。

+0

+1感謝您的貢獻。 - 完成我有一個不同的觀點 - 測量語句和函數的包含時間是一個目標,找出可以做什麼來使代碼更快是一個不同的目標,當代碼變得很大時,差異變得非常明顯 - 就像10^6行代碼,代碼可能會浪費大量的時間,我發現它的方式是抽取少量非常詳細的樣本,並用一個人的眼睛 - 不是總結。問題由於浪費時間的一小部分而暴露出來。 – 2015-02-02 14:18:12

+1

你說得對,當我想要分析一個較小的子集時,我沒有提及pprofile的用法。我編輯我的帖子來添加這個例子。 – vpelletier 2015-02-03 06:54:40

+0

這正是我所期待的:非侵入性和廣泛性。 – egpbos 2016-10-05 06:30:16

0

PyVmMonitor有一個實時視圖可以幫助你(你可以連接到正在運行的程序並從中獲取統計信息)。

請參見:http://www.pyvmmonitor.com/