Morikatron Engineer Blog

モリカトロン開発者ブログ

(高速化の前に) Python のプログラムをプロファイリングして遅い処理を特定しよう

こんにちは、モリカトロンでプログラマおじさんをやってる岡島です。

Python でプログラムを書いていると高速に実行したくなることが多々あると思います。 でも、「とにかく実行速度を最速に!」みたいな人は最初から Python なんて使わないですよね。

ということでプログラムの最適化にあんまり興味のない人が、出来るだけ手間をかけずそこそこ効果が得られる方法を紹介していこうと思います。

どの処理が遅いか特定する

プログラムの全てを最適化していくのは大変なので、まずはプロファイリングして最適化すべきポイントを特定しましょう。
時間のかかる処理だけ最適化すれば大体の場合は十分ですから。

最初に結論

Python 標準の cProfile を使ってプロファイリングして、snakeviz で見ます。

手順は以下の通り

python -m cProfile -o output.prof test.py
snakeviz output.prof

事前準備

短い時間で終了するようにする

機械学習だと何日もプログラムを実行し続けると思いますが、その全期間をプロファイリングするのはあまり得策とは言えません。
ひとまずは1エピソードでプログラムを終了させるようにしましょう。

マルチスレッド、マルチプロセスは扱わない

今回の方法だとマルチスレッドで計算させた場合にスレッド待ちの時間が見えてしまいます。 また cProfile は子プロセスまで含めてプロファイリングをしません。

ということで、今回はマルチスレッド、マルチプロセスは扱いません。

「そういう場合はどうするの?」という御意見があれば、そのうち調べて共有したいと思います。

プログラムを実行して解析する

test.py をプロファイリングして、結果を output.prof に書き出すには以下のようにします。

python test.py

python -m cProfile -o output.prof test.py

プロファイリングをする場合、通常よりも多少実行速度が遅くなります。

snakeviz で結果を見る

今のところ snakeviz という可視化ツールがオススメです。

SnakeViz は cProfile の出力結果 (上記だと output.prof) から 処理時間の統計を可視化してくれるツールです。 プログラム全体の中でどの処理が時間を食っているのかを目で見てわかるように表示してくれます。

使い方

インストール

pip 経由で簡単にインストールできます。

pip install snakeviz

プロファイル結果の確認

snakeviz に先ほど書き出したプロファイル結果指定するだけです。

snakeviz output.prof

実行するとお手持ちのWebブラウザにプロファイル結果が表示されます。

ブラウザで表示するので、Python 実行環境がクラウド上にある人は .prof をローカルにおいて実行しましょう。

結果の見方

チャート

f:id:morika-okajima:20200122154750g:plain
snakeviz は解析結果をこんな風に表示してくれます。

アイシクルツリーという形式で、どの処理が合計でどのくらいの時間を要したのかを図示してくれます。
呼び出した関数を下に積んでいくような形式で、「func_A() が呼び出している func_B() に時間がかかっている」というようなことが一目でわかるようになっています。

各セルをクリックすると、それをフォーカスしたより細かい情報が確認できるようになっていますので、マウスをポチポチ押しながら時間のかかっている処理の特定をしましょう。

統計データ

下の方に、関数ごとの統計データが表示されます。
こちらを参照すると、より細かい情報を知ることが出来ます。

f:id:morika-okajima:20200122155229p:plain
SnakeViz の統計データはこんな感じです。

名前 意味
ncalls 呼び出し回数
tottime 関数の処理時間の合計 (呼び出した関数の処理時間は除外)
percall ( tottime の隣) tottime を ncalls で割った値。一回の実行にかかる平均時間
cumtime 関数の処理時間の合計 (呼び出した関数の処理時間も含める)
percall (sumtime の隣) cumtime を ncalls で割った値。一回の実行にかかる平均時間
filename:lineno(function) ファイル名、行番号、関数名

より詳しく

もっと理解したい人は、以下を参考にすると良いでしょう。 qiita.com docs.python.org