Project Eulerやその他のコーディング コンテストでは、多くの場合、実行に最大の時間がかかったり、特定のソリューションの実行速度を自慢したりします。Python では、タイミング コードを .xml ファイルに追加するなど、アプローチがややぎこちない場合があります__main__
。
Python プログラムの実行にかかる時間をプロファイリングする良い方法は何ですか?
Project Eulerやその他のコーディング コンテストでは、多くの場合、実行に最大の時間がかかったり、特定のソリューションの実行速度を自慢したりします。Python では、タイミング コードを .xml ファイルに追加するなど、アプローチがややぎこちない場合があります__main__
。
Python プログラムの実行にかかる時間をプロファイリングする良い方法は何ですか?
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}
編集: Python Profiling
Also via YouTubeというタイトルの PyCon 2013 からの優れたビデオ リソースへのリンクを更新
しました。
少し前にpycallgraph
、Python コードからビジュアライゼーションを生成するものを作成しました。編集:この記事の執筆時点での最新リリースである 3.3 で動作するように例を更新しました。
GraphVizpip install pycallgraph
をインストールした後、コマンド ラインから実行できます。
pycallgraph graphviz -- ./mypythonscript.py
または、コードの特定の部分をプロファイリングできます。
from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput
with PyCallGraph(output=GraphvizOutput()):
code_to_profile()
pycallgraph.png
これらのいずれかで、以下の画像のようなファイルが生成されます。
プロファイラーの使用は (既定では) メイン スレッドでのみ機能し、他のスレッドを使用しても、他のスレッドからは情報を取得できないことに注意してください。これは、プロファイラーのドキュメントではまったく言及されていないため、ちょっとした問題になる可能性があります。
スレッドのプロファイリングも行いたい場合は、ドキュメントのthreading.setprofile()
関数を参照してください。
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
標準のクラスの代わりにそのクラスを使用します。柔軟性が向上する可能性がありますが、特にクラスを使用しないサードパーティのコードを使用している場合は、それだけの価値があるかどうかはわかりません。
python wiki はリソースのプロファイリングに最適なページです: http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code
pythonドキュメントと同様: http://docs.python.org/library/profile.html
Chris Lawlor が示すように、cProfile は優れたツールであり、画面への印刷に簡単に使用できます。
python -m cProfile -s time mine.py <args>
またはファイルする:
python -m cProfile -o output.file mine.py <args>
PS> Ubuntu を使用している場合は、必ず python-profile をインストールしてください。
apt-get install python-profiler
ファイルに出力する場合、次のツールを使用して素敵な視覚化を取得できます
PyCallGraph : コール グラフ イメージを作成するツール
インストール:
pip install pycallgraph
走る:
pycallgraph mine.py args
見る:
gimp pycallgraph.png
pngファイルを表示するには、好きなものを使用できます.gimpを使用しました
残念ながら、よく取得します
ドット: cairo-renderer ビットマップに対してグラフが大きすぎます。適合するように 0.257079 でスケーリング
これにより、画像が使用できないほど小さくなります。だから私は一般的にsvgファイルを作成します:
pycallgraph -f svg -o pycallgraph.svg mine.py <args>
PS> graphviz (dot プログラムを提供する) を必ずインストールしてください:
pip install graphviz
@maxy / @quodlibetor 経由で gprof2dot を使用した代替グラフ:
pip install gprof2dot
python -m cProfile -o profile.pstats mine.py
gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg
この回答に対する@Maxyのコメントは、それが独自の回答に値すると思うほど十分に役立ちました.cProfileで生成された.pstatsファイルが既にあり、pycallgraphで再実行したくなかったので、 gprof2dot を使用し、きれいになりましたsvg:
$ 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 が失う情報が少ないという印象を受けます。
また、言及する価値があるのは、GUI cProfile ダンプ ビューアーRunSnakeRunです。並べ替えと選択ができるため、プログラムの関連部分を拡大できます。写真の長方形のサイズは、所要時間に比例します。長方形の上にマウスを置くと、その呼び出しがテーブルとマップ上のすべての場所で強調表示されます。長方形をダブルクリックすると、その部分が拡大されます。誰がその部分を呼び出し、その部分が何を呼び出すかが表示されます。
説明的な情報は非常に役立ちます。組み込みのライブラリ呼び出しを扱うときに役立つ、そのビットのコードが表示されます。コードを検索するファイルと行がわかります。
また、OPが「プロファイリング」と言ったことを指摘したいのですが、彼は「タイミング」を意味していたようです。プロファイリングすると、プログラムの実行が遅くなることに注意してください。
優れたプロファイリングモジュールはline_profilerです(スクリプトkernprof.pyを使用して呼び出されます)。こちらからダウンロードできます。
私の理解では、cProfileは各関数で費やされた合計時間に関する情報のみを提供します。したがって、コードの個々の行のタイミングは調整されません。多くの場合、1つの行に多くの時間がかかる可能性があるため、これは科学計算の問題です。また、私が覚えているように、cProfileは私がnumpy.dotと言って過ごしていた時間を捕らえませんでした。
line_profiler
pprofile
(すでにここに示されています)また、次のように説明されている に触発 されました。
行粒度、スレッド対応の決定論的および統計的な純粋な Python プロファイラー
line_profiler
は純粋な Python であり、スタンドアロン コマンドまたはモジュールとして使用でき、さらには で簡単に分析できる callgrind 形式のファイルを生成できます[k|q]cachegrind
。
次のように説明されている Python パッケージであるvprofもあります。
[...] 実行時間やメモリ使用量など、さまざまな Python プログラムの特性について、豊富でインタラクティブな視覚化を提供します。
多くの優れた回答がありますが、それらはコマンドラインまたは外部プログラムを使用して、結果のプロファイリングおよび/または並べ替えを行います。
コマンドラインに触れたり、何もインストールしたりせずに、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)
詳細については、ドキュメントまたはその他の回答を参照してください。
マルチスレッド コードが期待どおりに動作しないという Joe Shaw の回答に続いてruncall
、cProfile のメソッドはプロファイルされた関数呼び出しを実行して呼び出すself.enable()
だけであると考えました。self.disable()
既存のコードへの干渉を最小限に抑えます。
Virtaal のソースには、プロファイリング (特定のメソッド/関数であっても) を非常に簡単にできる非常に便利なクラスとデコレータがあります。出力は、KCacheGrind で非常に快適に表示できます。
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も参照してください。
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]
Python スクリプトが何をしているのか知りたいと思ったことはありませんか? 検査シェルに入ります。Inspect Shell を使用すると、実行中のスクリプトを中断することなく、グローバルを印刷/変更し、関数を実行できます。オートコンプリートとコマンド履歴が追加されました (Linux のみ)。
Inspect Shell は pdb スタイルのデバッガーではありません。
https://github.com/amoffat/Inspect-Shell
あなたはそれ(そしてあなたの腕時計)を使うことができます。
cprofiler やその他のリソースは、デバッグよりも最適化を目的としていることがわかりました。
単純な Python スクリプトの速度テストの代わりに、独自のテスト モジュールを作成しました。(私の場合、1,000 行以上の py ファイルが ScriptProfilerPy を使用してテストされ、その後数分でコードが 10 倍高速化されました。
モジュール ScriptProfilerPy() は、タイムスタンプを追加してコードを実行します。ここにモジュールを配置します: https://github.com/Lucas-BLP/ScriptProfilerPy
使用する:
from speed_testpy import ScriptProfilerPy
ScriptProfilerPy("path_to_your_script_to_test.py").Profiler()