備忘録 blog

Docker/Machine Learning/Linux

Python2のコードを高速化するための覚書

実行時間の測定方法

CPythonでpythonの素のコードを実行するとヤバイぐらいに時間がかかる。実際にどのぐらいかかるのかを調べる。

import time
def main():
  start = time.time()
  hoge()
  elapsed_time = time.time() - start

まあこれでもよいのだが、

python -m cProfile -c totaltime hoge.py

以上を実行すれば、それぞれの関数呼び出しの回数と、totalの実行時間が明らかになる。明らかになったところで、律速段階となっている関数の高速化に取り組みたいと思う。

高速化手法

numbaでjitを使う

from numba.decorators import jit

@jit
def hogehoge():

@jitをつけるだけでllvmjitしてくれる。あまり早くならなかった。

Numba — Numba

numpy.vectorizeを使う

numpy.vectorizeは配列を引数に取って配列として返す関数を作るものである。ここで、対象となる関数は引数が複数あってもよく、それぞれの引数についてベクトルを渡せばそれぞれの値に対して処理された結果が帰ってくるし、スカラーを渡せばそれはどのベクトルに対しても同じ値が引数として呼ばれる。

import numpy as np

def hogehoge(t, anchor):
    return "hogehoge"

def main()
    vhogehoge = np.vectorize(hogehoge, excluded=['anchor'])
    mlist = list(vhogehoge(np.arange(0.0, 1.01, 0.01), anchor=anchor))

引数のexcludedでanchorを除外することで、引数に配列を渡したいときにはその配列が分解されずにその関数に渡すことができる。 一方で、渡したいarray of arrayとなっている場合、引数に渡した配列excludedされては"array of array"のまま渡されるか、さもなければ1つのcellごと渡されることになるので、二次元配列を一次元分だけ渡すということはできない。

pypyを使う

これが現時点では最も効果が得られた。 pypyは、CPythonではないpythonの処理系の実装であり、JITコンパイルを適宜行って実行速度の改善が図られている。但しpypyではCPythonのCバインディングのところや、ライブラリの対応がまだ中途半端ということらしいが少なくとも、python2~3の互換性よりはpypyの方がスムーズに移行できた。つまり、コードの書き換えをほとんど必要としないということである。 実行するときは、公式サイトからダウンロードしたバイナリファイル(bin/pypy)をpythonの代わりに実行すればよい。 これを用いると、あるプログラムで11倍ほど高速化された。

pypy.org

Boost.Pythonを使う

これはpythonの範疇からはみ出してしまうが、C++でコードを書き、それをpythonでラッパーとして呼び出して実行する方法としてBoost.Pythonがある。 これについてはまだ研究中であり、ここでの詳述は避けたい。

http://alpha.osdn.jp/devel/boost.python_ja.pdf