pythonアプリケーションのパフォーマンス改善をする機会があったので、
その時の調査で役に立ったライブラリの使い方を、なるべく簡単に説明したいと思います。
今回紹介するのは、この3つです。
line_profiler・・・実行速度を計測するライブラリ
memory_profiler・・・メモリ使用量を計測するライブラリ
pdb・・・プログラムのデバッガ
全てIPythonのコンソールで使えるライブラリなので、コンソールで使い方を説明してきたいと思います。
##line_profilerの使い方
line_profilerをインストールします。
$ pip install line_profiler
IPythonのコンソールを起動します。
(line_profilerのインストール時にIPythonも同梱されています。)
$ ipython
実行速度を計測するメソッドをコンソール上で作成します。
import time
def hoge():
# 0.01秒スリープさせる
time.sleep(0.01)
# 10000回ループさせる
for i in range(10000):
pass
コンソール上でline_profilerのコマンドを使えるようにします。
load_ext line_profiler
line_profilerのlprunコマンドでメソッドの実行速度を計測します。
lprun -f hoge hoge()
実行結果が表示されます。
Timer unit: 1e-06 s
Total time: 0.01848 s
File: <ipython-input-22-ac529d19217f>
Function: hoge at line 2
Line # Hits Time Per Hit % Time Line Contents
==============================================================
2 def hoge():
3 # 0.01秒スリープさせる
4 1 10064 10064.0 54.5 time.sleep(0.01)
5 # 10000回ループさせる
6 10001 4574 0.5 24.8 for i in range(10000):
7 10000 3842 0.4 20.8 pass
当たり前ですが、time.sleepで約0.01秒かかったことがわかります。
ループが10000回行われたこともHits列でわかります。
Time列の数値が高いところが時間のかかった処理ということになるので、そこの処理を見直すと処理速度の改善につながると思います。
引数のあるメソッドの場合は、このように書きます。
import time
def hoge(loop_count):
# 0.01秒スリープする
time.sleep(0.01)
# 引数の数分ループさせる
for i in range(loop_count):
pass
lprun -f hoge hoge(10000)
##memory_profilerの使い方
memory_profilerをインストールします。
$ pip install memory_profiler
IPythonをインストールします。
$ pip install ipython
メモリ使用量を計測するメソッドを書いたファイルを作成します。
(memory_profilerはコンソール上で作成したメソッドは計測できないので。)
hoge.py
def hoge():
# 100000個の配列を作る
l = list()
for i in range(100000):
l.append(i)
# 100000個の辞書を作る
d = dict()
for i in range(100000):
d[i] = None
メソッドを作ったらIPythonのコンソールを起動します
起動時に先ほど作成したhoge.pyファイルを読み込ませます。
$ ipython -i hoge.py
コンソール上でmemory_profilerのコマンドを使えるようにします。
load_ext memory_profiler
memory_profilerのmprunコマンドでメソッドのメモリ使用量を計測します。
mprun -f hoge hoge()
実行結果が表示されます。
Line # Mem usage Increment Line Contents
================================================
1 34.7 MiB 0.0 MiB def hoge():
2 # 100000個の配列を作る
3 34.7 MiB 0.0 MiB l = list()
4 37.7 MiB 3.0 MiB for i in range(100000):
5 37.7 MiB 0.0 MiB l.append(i)
6 # 100000個の辞書を作る
7 37.7 MiB 0.0 MiB d = dict()
8 49.1 MiB 11.3 MiB for i in range(100000):
9 49.1 MiB 0.0 MiB d[i] = None
配列の作成に3.0MiB、辞書の作成に11.3MiBのメモリが使われたことがわかりました。
引数のあるメソッドの場合は、このように書きます。
def hoge(loop_count):
l = list()
for i in range(loop_count):
l.append(i)
d = dict()
for i in range(loop_count):
d[i] = None
mprun -f hoge hoge(100000)
##pdbの使い方
pdbはpythonに最初から付属されているので特にインストールする必要はありません。
使い方は簡単で、デバッグを開始したい行に以下のコードを挿入するだけです。
import pdb; pdb.set_trace()
コンソール上で適当なメソッドを作って、先ほどのコードを挿入します。
def hoge():
var = "fuga"
print (var)
import pdb; pdb.set_trace()
print (var)
hogeメソッドを実行すると、pdb.set_trace()で処理が止まり、pdbのコマンド入力待ち状態となります。
In [2]: hoge()
fuga
> <ipython-input-1-3d0c1a13d7af>(5)hoge()
-> print (var)
(Pdb)
pコマンドで変数varの内容が確認できます。
(Pdb) p var
'fuga'
途中で変数varの内容を変更することもできます。
(Pdb) var = "piyo"
(Pdb) p var
'piyo'
あとは以下のコマンドを使ってステップ実行しながら確認するとデバッグしやすいと思います。
コマンド | 説明 |
---|---|
s(tep) | 現在の行を実行します。実行する行が関数やメソッドの場合ステップインします。 |
n(ext) | 現在の行を実行します。実行する行が関数やメソッドの場合ステップインしません。 |
c(ontinue) | 次のブレークポイント(pdb.set_trace())まで実行を継続します。 |
l(ist) | 現在のソースコードを表示します。 |
ちなみに、eclipseのPyDevを使うとGUIでデバッグできて便利みたいです。
eclipseを使っていないという人や、サーバーのコンソール上で確認することがある人はpdbを使ってみるといいと思います。