Python kodunu satır satır nasıl profilleyebilirim?


116

Kodumun profilini çıkarmak için cProfile kullanıyorum ve harika çalışıyor. Sonuçları görselleştirmek için gprof2dot.py'yi de kullanıyorum (biraz daha net hale getiriyor).

Ancak, cProfile (ve şimdiye kadar gördüğüm diğer çoğu Python profil oluşturucusu) yalnızca işlev çağrısı düzeyinde profil oluşturuyor gibi görünüyor. Bu, belirli işlevler farklı yerlerden çağrıldığında kafa karışıklığına neden oluyor - çoğu zaman 1 numaralı çağrının mı yoksa 2 numaralı çağrının mı yapıldığı konusunda hiçbir fikrim yok. Söz konusu işlev, yedi farklı yerden çağrılan altı seviye derin olduğunda bu daha da kötüleşir.

Nasıl satır satır profil oluşturabilirim?

Bunun yerine:

function #12, total time: 2.0s

Bunun gibi bir şey görmek isterim:

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

cProfile, toplam sürenin ne kadarının ebeveyne "aktarıldığını" gösterir, ancak yine bir grup katman ve birbirine bağlı aramalarınız olduğunda bu bağlantı kesilir.

İdeal olarak, verileri ayrıştıracak bir GUI'ye sahip olmayı ve ardından bana her satıra verilen toplam süreyi içeren kaynak dosyamı göstermeyi çok isterim. Bunun gibi bir şey:

main.py:

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

Daha sonra, "func (a)" çağrısından ayrı olarak, bu çağrıda neyin sürdüğünü görmek için ikinci "func (c)" çağrısına tıklayabilirim.

bu mantıklı mı? Bu tür bilgileri toplayan herhangi bir profil oluşturma kitaplığı var mı? Kaçırdığım harika bir araç var mı?


2
Benim tahminim ilginizi çekeceği yönünde pstats.print_callers. Burada bir örnek var .
Muhammad Alkarouri

Muhammed, bu kesinlikle yardımcı oldu! En azından bir sorunu çözer: kökene bağlı olarak işlev çağrılarını ayırmak. Joe Kington'ın cevabının amacıma daha yakın olduğunu düşünüyorum, ancak print_callers () kesinlikle beni oraya götürüyor. Teşekkürler!
rocketmonkeys

Yanıtlar:


120

Robert Kern'in line_profiler'ının amaçlandığı şeyin bu olduğuna inanıyorum . Bağlantıdan:

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

Umarım yardımcı olur!


10
Line_profiler Python 3 ile çalışıyor mu? Bununla ilgili herhangi bir bilgi alamadım.
user1251007

3
line_profiler benim için isabet ve zamanı göstermiyor. Biri bana nedenini söyleyebilir mi? Ve nasıl çözülür?
I159

6
İşte yazdığım dekoratör: gist.github.com/kylegibson/6583590 . Eğer nosetestler çalıştırıyorsanız, stdout'un hemen yazdırılması için -s seçeneğini kullandığınızdan emin olun.
Kyle Gibson

5
Bu çıktıyı üreten python betiği nasıl görünüyor? import line_profiler;ve sonra ?
Zhubarb

10
bu kitaplığın nasıl kullanılacağını gösteren var mı? Benioku dosyası nasıl kurulacağını öğretir ve çeşitli SSS'leri yanıtlar, ancak bir pip kurulumundan sonra nasıl kullanılacağından bahsetmez ..
cryanbhu

47

Pprofile ( pypi ) ' yi de kullanabilirsiniz . Tüm yürütmeyi profillemek istiyorsanız, kaynak kodu değişikliği gerektirmez. Daha büyük bir programın bir alt kümesinin profilini iki şekilde de yapabilirsiniz:

  • Kodda belirli bir noktaya ulaşırken profil oluşturmayı değiştirin, örneğin:

    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")
  • İstatistiksel profilleme kullanarak profil oluşturmayı çağrı yığınından eşzamansız olarak değiştirin (söz konusu uygulamada bu kodu tetiklemek için bir yol gerektirir, örneğin bir sinyal işleyici veya kullanılabilir bir çalışan iş parçacığı):

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

Kod ek açıklama çıktı biçimi, satır profilleyicisine çok benzer:

$ 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 kod değişikliğine bağlı olmadığından, üst düzey modül ifadelerinin profilini oluşturarak program başlatma süresinin profilini çıkarabilir (modülleri içe aktarmak, globalleri başlatmak, ...).

Önbellek biçimli çıktı oluşturabilir, böylece büyük sonuçlara kolayca göz atmak için kcachegrind'i kullanabilirsiniz .

Açıklama: Ben pprofile yazarıyım.


1
+1 Katkınız için teşekkür ederiz. İyi yapılmış görünüyor. Biraz farklı bakış açım var - ifadeler ve işlevler tarafından harcanan kapsayıcı zamanı ölçmek bir amaçtır. Kodu daha hızlı hale getirmek için neler yapılabileceğini bulmak farklı bir amaçtır. Kod büyüdükçe, 10 ^ 6 satır kod gibi, fark acı verici bir şekilde bariz hale gelir. Kod büyük oranda zaman kaybediyor olabilir. Bunu bulmamın yolu, az sayıda çok ayrıntılı örnek alıp bunları özetlemek değil, insan gözüyle incelemektir. Sorun, harcadığı zamanın oranıyla ortaya çıkar.
Mike Dunlavey

1
Haklısın, daha küçük bir alt kümeyi profillemek istediğinde pprofile kullanımından bahsetmedim. Bunun örneklerini eklemek için gönderimi düzenledim.
vpelletier

3
Tam olarak aradığım şey buydu: müdahaleci olmayan ve kapsamlı.
egpbos

1
Güzel bir araç, ancak orijinal koddan birkaç kat daha yavaş çalışıyor.
rominf

4

Bunun için line_profiler paketinden yardım alabilirsiniz.

1. 1. paketi kurun:

    pip install line_profiler

2. Paketi python / dizüstü ortamınıza yüklemek için sihirli komutu kullanın

    %load_ext line_profiler

3. Bir işlevin kodlarının profilini çıkarmak istiyorsanız
, aşağıdaki işlemleri yapın:

    %lprun -f demo_func demo_func(arg1, arg2)

Bu adımları izlerseniz tüm detaylarıyla güzel bir formatlanmış çıktı elde edersiniz :)

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'ın yukarıda bahsedilen cevabını iyileştirmek için .

İçin Python 3.x , kullanım line_profiler :


Kurulum:

pip install line_profiler

Kullanımı:

Programa sahip olduğunuzu main.pyve içinde işlev gördüğünüzü fun_a()ve fun_b()zamana göre profil oluşturmak istediğinizi varsayalım ; @profileişlev tanımlarından hemen önce dekoratör kullanmanız gerekecektir . Örneğin,

@profile
def fun_a():
    #do something

@profile
def fun_b():
    #do something more

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

Programın profili, kabuk komutu çalıştırılarak oluşturulabilir:

$ kernprof -l -v main.py

Argümanlar kullanılarak getirilebilir $ 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.

Sonuçlar konsolda şu şekilde yazdırılacaktır:

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():
...

DÜZENLEME: Profil oluşturuculardan gelen sonuçlar TAMPPA paketi kullanılarak ayrıştırılabilir . Bunu kullanarak, istenen grafikleri satır satır alabiliriz. arsa


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.