Cython を使ってみる2016年2月14日 | |
はじめにPython コードを C に変換してコンパイルする Cython について、Micha Gorelick, Ian Ozsvald 著 "ハイパフォーマンス Python" の内容に沿って試してみたメモ。 環境
※MinGW にも Python が入っているので、Anaconda のほうを使うようにパスを設定している。 Cython は Anaconda に含まれている。バージョンは 0.23.4。 サンプルコードサンプルコードは こちら から入手。 ここでは以下のものを用いる。 07_compiling/cython/lists/1/
Cython によるコンパイルCython によるコンパイルは次の手順で行う。
setup.py の中身は以下の通り。 from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext setup( cmdclass={'build_ext': build_ext}, ext_modules=[Extension("calculate", ["cythonfn.pyx"])] ) 次のようにコンパイルする。 $ python setup.py build_ext --inplace オプションの "--inplace" は、モジュールをカレントディレクトリに生成するように指示するもの。うまく行けば、Windows の場合は ".pyd" ファイルが、Linux の場合は ".so" ファイルができる。 サンプルを実行すると、次のようなエラーが出た。 $ python setup.py build_ext --inplace running build_ext cythoning cythonfn.pyx to cythonfn.c building 'calculate' extension error: Unable to find vcvarsall.bat これは、Visual C++ の環境を探したけど見つからなかったことを意味している。ここでは、以下のコンパイル環境を試してみる。
Microsoft Visual C++ Compiler for Python 2.7Microsoft が こちら に Python 用のコンパイル環境を用意してくれているので、これを導入する。 インストールすると、なぜかユーザーフォルダの "AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0" にインストールされる。インストールしただけでは Python が認識してくれないので、Python フォルダの "Lib\distutils\msvc9compiler.py" に設定を追記する。このファイルの関数 find_vcvarsall() の最後の "if not productdir" の前に、以下のように productdir に Visual C++ for Python のパスを設定する。※(ユーザー名) は自分のユーザー名に置き換えること。def find_vcvarsall(version): ... productdir = "C:\\Users\\(ユーザー名)\\AppData\\Local\\Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0" if not productdir: log.debug("No productdir found") return None ... MinGWMinGW を用いる場合は、Python フォルダの "Lib\distutils" に "distutils.cfg" という名前で以下の内容のファイルを置いておく。 [build] compiler = mingw32 これだけだとライブラリをリンクできないので、以下のように MinGW でライブラリを変換する。 $ gendef /c/Anaconda2/python27.dll $ dlltool -d python27.def -l libpython27.dll.a $ cp libpython27.dll.a /c/Anaconda2/libs/ VC++ でも MinGW でも好きな方でよいのだが、line_profiler を使う場合は、line_profiler が VC++ を使っているため、VC++ を使わざるを得ない。 Cython による最適化もとのコードの計算結果は以下の通り。 $ python julia1_nopil.py Length of x: 1000 Total elements: 1000000 calculate_z_serial_purepython took 12.013999939 seconds Cython でコンパイルした結果は以下の通り。 $ python julia1.py Length of x: 1000 Total elements: 1000000 Took 6.93200016022 seconds Total sum of elements (for validation): 33219980 確かに速くなっている。 文献では、もとのコードに "cdef" で変数の型情報を追加すると速くなるとある。 def calculate_z(maxiter, zs, cs): """Calculate output list using Julia update rule""" cdef unsigned int i, n cdef double complex z, c output = [0] * len(zs) for i in range(len(zs)): n = 0 z = zs[i] c = cs[i] while n < maxiter and abs(z) < 2: z = z * z + c n += 1 output[i] = n return output 結果。 $ python julia1.py Length of x: 1000 Total elements: 1000000 Took 7.83299994469 seconds Total sum of elements (for validation): 33219980 遅くなった。文献ではさらに、abs() を直接計算に置き換えている。 while n < maxiter and (z.real*z.real + z.imag*z.imag) < 4: 結果。 $ python julia1.py Length of x: 1000 Total elements: 1000000 Took 3.34500002861 seconds Total sum of elements (for validation): 33219980 確かにだいぶ速くなったが、文献ほどではない。 Cython の変換情報Cython では、以下のようにして Python を C に変換できる。 $ cython -a cythonfn.pyx これにより、変換情報の HTML ファイルが生成される。 ![]() 黄色が濃い行ほど、Python の呼び出しが多いことを意味しており、最適化の余地がありそうなことを示している。 Jupyter Notebook における Cython の利用Jupyter Notebook では、もっと簡単に Cython を利用できる。まず、次のコマンドで Cython 拡張を読み込む。 %load_ext Cython "%%cython" を付けて Cython のコードを書いて実行する。 %%cython def calculate_z(maxiter, zs, cs): """Calculate output list using Julia update rule""" output = [0] * len(zs) for i in range(len(zs)): n = 0 z = zs[i] c = cs[i] while n < maxiter and abs(z) < 2: z = z * z + c n += 1 output[i] = n return output "%%cython" の代りに "%%cython --annotate" とすれば、Cython の変換情報が得られる。 参考文献 | |
PENGUINITIS |