pythonのパフォーマンス調査で使えるライブラリの紹介

pythonアプリケーションのパフォーマンス改善をする機会があったので、
その時の調査で役に立ったライブラリの使い方を、なるべく簡単に説明したいと思います。

今回紹介するのは、この3つです。

**[line_profiler](https://github.com/rkern/line_profiler)**・・・実行速度を計測するライブラリ
**[memory_profiler](https://pypi.python.org/pypi/memory_profiler)**・・・メモリ使用量を計測するライブラリ
**[pdb](http://docs.python.jp/2/library/pdb.html)**・・・プログラムのデバッガ

全てIPythonのコンソールで使えるライブラリなので、コンソールで使い方を説明してきたいと思います。

##line_profilerの使い方

line_profilerをインストールします。

```
＄ pip install line_profiler
```

IPythonのコンソールを起動します。
（line_profilerのインストール時にIPythonも同梱されています。）

```
$ ipython
```

実行速度を計測するメソッドをコンソール上で作成します。

```python
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列の数値が高いところが時間のかかった処理ということになるので、そこの処理を見直すと処理速度の改善につながると思います。

引数のあるメソッドの場合は、このように書きます。

```python
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

```python
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のメモリが使われたことがわかりました。

引数のあるメソッドの場合は、このように書きます。

```python
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()
```

コンソール上で適当なメソッドを作って、先ほどのコードを挿入します。

```python
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を使ってみるといいと思います。