💡

CythonによるPythonの高速化!!

2022/07/14に公開

公式やpipコマンドのご紹介

https://github.com/cython/cython

pip install Cython

ビルドする

1. 対象のPythonのコードを「pyx」で保存する。

test_cython.pyx
for i in range(1, 100000000):
    i = i + 1

print(i)

2. setup.pyを作成する。

setup.py
from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules=cythonize("test_cython.pyx")
)

3.ビルドする。

python setup.py build_ext --inplace

⚠エラーが出た場合

error: Microsoft Visual C++ 14.0 is required. Get it with "Build Tools for Visual Studio": https://visualstudio.microsoft.com/downloads/

URLのページに進む

※筆者は簡単にインストールを済ませるために「C++によるデスクトップ開発」にチェックを入れてインストールを行いました。

ビルド完了後

buildフォルダが生成され、pydファイルとその他もろもろが生成されます。(Windowsの場合)

4.利用してみよう

test.py
import test_cython
実行コマンド
python test.py

うまくコマンドが実行できましたね。

速度の検証

https://zenn.dev/timoneko/articles/16f9ee7113f3cd
上記のデコレータを使用して速度を比較してみましょう。

通常のPython
import pstats
import cProfile


def execution_speed_lib(func):
    """
    実行速度計測用のデコレータ
    """
    def wrapper(*args, **kwargs):
        pr = cProfile.Profile()
        # 実行処理の計測
        pr.runcall(func, *args, **kwargs)

        stats = pstats.Stats(pr)
        stats.print_stats()
    return wrapper


@execution_speed_lib
def run():
    for i in range(1, 100000000):
        i = i + 1

    print(i)


run()

私の環境では5.349 ~ 5.44秒前後でした。

pydをimportするパターン
import pstats
import cProfile


def execution_speed_lib(func):
    """
    実行速度計測用のデコレータ
    """
    def wrapper(*args, **kwargs):
        pr = cProfile.Profile()
        # 実行処理の計測
        pr.runcall(func, *args, **kwargs)

        stats = pstats.Stats(pr)
        stats.print_stats()
    return wrapper


@execution_speed_lib
def run():
    import test_cython


run()

こちらも5.44秒程度でした。速度はほぼ同じですね

ちなみに..test_cython.pyxの書き方を少し変えて再度ビルドしてみます。

test_cython.pyx
cdef int i
for i in range(1, 100000000):
    i = i + 1

print(i)
結果
100000000
         100 function calls in 0.011 seconds

   Random listing order was used

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:36(_relax_case)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:58(__init__)
....

0.011秒になりました。速い...!!
cythonを利用する場合はかなり書き方に気を付けないと早くならないようですね。
他の関数やクラスでCythonを使用して爆速になった方は、コードとともに感想をお待ちしています。(すでに高速化されていいるnumpyはいったん見ないふりをしています。)

Discussion