Pythonスクリプトをベンチマークする簡単な方法はありますか?


82

通常、私はシェルコマンドを使用しますtime。私の目的は、データが小、中、大、または非常に大きいセットであるかどうか、どのくらいの時間とメモリ使用量になるかをテストすることです。

これを行うためのLinuxまたはPython用のツールはありますか?

回答:


120

見ていはtimeitPythonのプロファイラpycallgraphを。また、「SnakeViz」に言及して、以下のコメントnikiccを確認してください。これにより、プロファイリングデータのさらに別の視覚化が可能になります。

timeit

def test():
    """Stupid test function"""
    lst = []
    for i in range(100):
        lst.append(i)

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))

    # For Python>=3.5 one can also write:
    print(timeit.timeit("test()", globals=locals()))

基本的に、Pythonコードを文字列パラメーターとして渡すことができ、指定された回数で実行され、実行時間を出力します。ドキュメントからの重要なビット:

timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)Timer指定されたステートメント、セットアップ コード、 タイマー関数を使用してインスタンスを 作成し、そのtimeitメソッドを 数回実行して実行します。オプションのglobals引数は、コードを実行する名前空間を指定します。

...そして:

Timer.timeit(number=1000000) 時間数のメイン文の実行。これにより、セットアップステートメントが1回実行され、次にメインステートメントの実行にかかる時間がfloatとして秒単位で測定されて返されます。引数はループを通過する回数であり、デフォルトは100万回です。使用するmainステートメント、setupステートメント、timer関数がコンストラクターに渡されます。

注: デフォルトでは、タイミング中にtimeit一時的にオフgarbage collectionになります。このアプローチの利点は、独立したタイミングをより比較できるようにすることです。この欠点は、GCが測定される機能のパフォーマンスの重要な要素である可能性があることです。その場合、セットアップ文字列の最初のステートメントとしてGCを再度有効にすることができます。例えば:

timeit.Timer('for i in xrange(10): oct(i)', 'gc.enable()').timeit()

プロファイリング

プロファイリングはあなたを与えるだろうくらいに何が起こっているのかについてのより詳細なアイデアを。これが公式ドキュメントの「インスタント例」です。

import cProfile
import re
cProfile.run('re.compile("foo|bar")')

それはあなたに与えるでしょう:

      197 function calls (192 primitive calls) in 0.002 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.000    0.000    0.001    0.001 <string>:1(<module>)
     1    0.000    0.000    0.001    0.001 re.py:212(compile)
     1    0.000    0.000    0.001    0.001 re.py:268(_compile)
     1    0.000    0.000    0.000    0.000 sre_compile.py:172(_compile_charset)
     1    0.000    0.000    0.000    0.000 sre_compile.py:201(_optimize_charset)
     4    0.000    0.000    0.000    0.000 sre_compile.py:25(_identityfunction)
   3/1    0.000    0.000    0.000    0.000 sre_compile.py:33(_compile)

これらのモジュールは両方とも、ボトルネックを探す場所についてのアイデアを提供するはずです。

また、の出力を把握するにはprofileこの投稿をご覧ください

pycallgraph

pycallgraphは、2018年2月から正式に廃止されました。2020年12月の時点では、Python3.6でまだ動作していました。PythonがプロファイリングAPIを公開する方法にコアの変更がない限り、それは有用なツールであり続けるはずです。

このモジュールはgraphvizを使用して、次のようなcallgraphを作成します。

コールグラフの例

どのパスが最も時間を使用したかを色で簡単に確認できます。pycallgraph APIを使用するか、パッケージ化されたスクリプトを使用して作成できます。

pycallgraph graphviz -- ./mypythonscript.py

ただし、オーバーヘッドはかなりのものです。そのため、すでに長時間実行されているプロセスの場合、グラフの作成には時間がかかることがあります。


10
cProfileを使用する場合は、スクリプト全体をプロファイリングし、結果をpython -m cProfile -o results.prof myscript.py。を使用してファイルに保存するオプションもあります。oputputファイルは、非常にきれいと呼ばれるプログラムで、ブラウザで提示することができますSnakeViz使用してsnakeviz results.prof
nikicc

pycallgraphの最後のリリースは2013年、2018
Boris

@Borisそれは知っておくと良いことです。私は実際に昨日それを使用しました、そして-少なくとも今のところ-それはまだ機能します。投稿を更新します。情報をありがとう。
exhuma

28

シンプルなデコレータを使用して関数の時間を計ります

def st_time(func):
    """
        st decorator to calculate the total time of a func
    """

    def st_func(*args, **keyArgs):
        t1 = time.time()
        r = func(*args, **keyArgs)
        t2 = time.time()
        print "Function=%s, Time=%s" % (func.__name__, t2 - t1)
        return r

    return st_func

もちろん、print "Function =%s、Time =%s"%(func .__ name __、t2-t1)です。本当に便利です
user1941126 2014

17

timeit私がこれを書いたように、モジュールは、遅いと奇妙でした。

def timereps(reps, func):
    from time import time
    start = time()
    for i in range(0, reps):
        func()
    end = time()
    return (end - start) / reps

例:

import os
listdir_time = timereps(10000, lambda: os.listdir('/'))
print "python can do %d os.listdir('/') per second" % (1 / listdir_time)

私にとって、それは言う:

python can do 40925 os.listdir('/') per second

これは原始的な種類のベンチマークですが、それで十分です。


7
@exhuma、私は詳細を忘れて、多分私の評価で急いでいました!コードの2つのチャンクを(関数/ラムダではなく)文字列として受け取るため、「奇妙な」と言ったと思います。しかし、コードの非常に短期間のセグメントのタイミングをとるときに、その価値を見ることができます。デフォルトで1,000,000ループになっているので、「遅い」と言ったと思いますが、それを調整する方法を検討していませんでした。私のコードがすでに担当者の数で割られているのが好きです。しかし、timeitは間違いなくより良い解決策です、私はそれを捨てたことをお詫びします。
サムワトキンス

11

私は通常、time ./script.pyどれくらいの時間がかかるかを簡単に確認します。ただし、少なくともデフォルトとしては、メモリは表示されません。/usr/bin/time -v ./script.pyメモリ使用量など、多くの情報を取得するために使用できます。


1
ただ、このコマンドを、覚えている/usr/bin/timeとの-vオプションは、多くのディストリビューションのデフォルトとして使用することはできませんインストールする必要があります。sudo apt-get install timedebian、ubuntuなどでpacman -S time。archlinux–
Rui Andrada

6

すべてのメモリニーズに対応するメモリプロファイラー。

https://pypi.python.org/pypi/memory_profiler

pipインストールを実行します。

pip install memory_profiler

ライブラリをインポートします。

import memory_profiler

プロファイルするアイテムにデコレータを追加します。

@profile
def my_func():
    a = [1] * (10 ** 6)
    b = [2] * (2 * 10 ** 7)
    del b
    return a

if __name__ == '__main__':
    my_func()

コードを実行します。

python -m memory_profiler example.py

出力を受け取ります:

 Line #    Mem usage  Increment   Line Contents
 ==============================================
 3                           @profile
 4      5.97 MB    0.00 MB   def my_func():
 5     13.61 MB    7.64 MB       a = [1] * (10 ** 6)
 6    166.20 MB  152.59 MB       b = [2] * (2 * 10 ** 7)
 7     13.61 MB -152.59 MB       del b
 8     13.61 MB    0.00 MB       return a

例は、上記のリンク先のドキュメントからのものです。


3

とそのプラグインの1つこれを見てください、特に。

インストールすると、noseはパス内のスクリプトになり、いくつかのpythonスクリプトを含むディレクトリで呼び出すことができます。

$: nosetests

これにより、現在のディレクトリ内のすべてのpythonファイルが検索され、テストとして認識される関数が実行されます。たとえば、名前にtest_という単語が含まれる関数がテストとして認識されます。

したがって、test_yourfunction.pyというPythonスクリプトを作成して、次のように記述できます。

$: cat > test_yourfunction.py

def test_smallinput():
    yourfunction(smallinput)

def test_mediuminput():
    yourfunction(mediuminput)

def test_largeinput():
    yourfunction(largeinput)

次に、実行する必要があります

$: nosetest --with-profile --profile-stats-file yourstatsprofile.prof testyourfunction.py

プロファイルファイルを読み取るには、次のPython行を使用します。

python -c "import hotshot.stats ; stats = hotshot.stats.load('yourstatsprofile.prof') ; stats.sort_stats('time', 'calls') ; stats.print_stats(200)"

これは、標準のPythonライブラリのプロファイラーと同じように思えます。テストは問題のトピックではありませんでした。プラス:noseホットショットに依存しています。Python 2.5以降は維持されておらず、「特殊な使用のために」のみ保持されています
exhuma 2009年

2

注意してくださいtimeit非常に遅いです、ちょうど初期化する(または多分関数を実行する)ために私の中型プロセッサで12秒かかります。あなたはこの受け入れられた答えをテストすることができます

def test():
    lst = []
    for i in range(100):
        lst.append(i)

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test")) # 12 second

time代わりに使用する簡単なものの場合、PCでは結果が返されます0.0

import time

def test():
    lst = []
    for i in range(100):
        lst.append(i)

t1 = time.time()

test()

result = time.time() - t1
print(result) # 0.000000xxxx

1
timeit関数を度も実行して、ノイズを平均化します。繰り返しの数はオプションです。Pythonでの実行時間のベンチマークまたはこの質問で受け入れられた回答の後半を参照してください。
ピーター

1

関数をすばやくテストする簡単な方法は、次の構文を使用することです。 %timeit my_code

例えば ​​:

%timeit a = 1

13.4 ns ± 0.781 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

1

snakeviz cProfileのインタラクティブビューア

https://github.com/jiffyclub/snakeviz/

cProfileはhttps://stackoverflow.com/a/1593034/895245で言及され、snakevizはコメント言及されましたが、私はそれをさらに強調したいと思いました。

cprofile/を見ただけでプログラムのパフォーマンスをデバッグするのは非常に困難です。pstats出力をが箱から出してすぐに機能ごとに合計できる回数しかできないためです。

ただし、一般的に本当に必要なのは、各呼び出しのスタックトレースを含むネストされたビューを表示して、実際に主要なボトルネックを簡単に見つけることです。

そして、これはまさにsnakevizがデフォルトの「つらら」ビューを介して提供するものです。

まず、cProfileデータをバイナリファイルにダンプする必要があります。次に、その上でsnakevizを実行できます。

pip install -u snakeviz
python -m cProfile -o results.prof myscript.py
snakeviz results.prof

これにより、ブラウザで開くことができるstdoutへのURLが出力されます。これには、次のような目的の出力が含まれています。

ここに画像の説明を入力してください

そして、次のことができます。

  • 各ボックスにカーソルを合わせると、関数を含むファイルへのフルパスが表示されます
  • ボックスをクリックすると、ズームインする方法としてそのボックスが上部に表示されます。

よりプロファイル指向の質問:Pythonスクリプトをどのようにプロファイルできますか?


-1

timeitの定型コードを記述せず、結果を簡単に分析したくない場合は、benchmarkitを参照してください。また、以前の実行の履歴が保存されるため、開発の過程で同じ機能を簡単に比較できます。

# pip install benchmarkit

from benchmarkit import benchmark, benchmark_run

N = 10000
seq_list = list(range(N))
seq_set = set(range(N))

SAVE_PATH = '/tmp/benchmark_time.jsonl'

@benchmark(num_iters=100, save_params=True)
def search_in_list(num_items=N):
    return num_items - 1 in seq_list

@benchmark(num_iters=100, save_params=True)
def search_in_set(num_items=N):
    return num_items - 1 in seq_set

benchmark_results = benchmark_run(
   [search_in_list, search_in_set],
   SAVE_PATH,
   comment='initial benchmark search',
)  

ターミナルに出力し、最後の実行のデータを含む辞書のリストを返します。コマンドラインエントリポイントも利用できます。

ここに画像の説明を入力してください

変更N=1000000して再実行した場合

ここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.