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をつけるだけでllvmでjitしてくれる。あまり早くならなかった。
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倍ほど高速化された。
Boost.Pythonを使う
これはpythonの範疇からはみ出してしまうが、C++でコードを書き、それをpythonでラッパーとして呼び出して実行する方法としてBoost.Pythonがある。 これについてはまだ研究中であり、ここでの詳述は避けたい。