MyEnigma

とある自律移動システムエンジニアのブログです。#Robotics #Programing #C++ #Python #MATLAB #Vim #Mathematics #Book #Movie #Traveling #Mac #iPhone

初心者のためのCython入門


Cython ―Cとの融合によるPythonの高速化

目次

はじめに

Pythonは、非常に沢山のライブラリが存在し、

プログラミング初心者でも簡単にコードを書くことができることから、

非常に利用が広がっていますが、

処理速度が遅いという欠点があります。

 

この問題を解決する方法の一つとして、

処理のボトルネックになっている部分を、

C言語などのコンパイル言語を使うことで、

高速化する方法がありますが、

そのように、既存のC言語のコードとPythonを連携させたり、

Pythonのコードに少し型情報などを追加することで、

高速化することができるのが、Cythonです。

cython.org

 

今回の記事では、Cythonの概要と簡単な使い方を紹介したいと思います。

Cythonとは?

cythonのPythonのスーパセットである言語と、

その言語で書かれたコードから、

c言語のコードを生成するトランスパイラのことを指します。

 

Cythonは通常のpipを使って、インストールすることができます。

$ pip install Cython

pypi.org

現在は0.x系が安定版ですが、次の世代として、3.x系が開発中です。

言語としてのcython

cythonはpythonにC言語の型を追加したものであると言えます。

これにより、通常のPythonよりも高速な計算が可能になります。

 

また、前述の通り、cythonはpythonのスーパーセットの言語なので、

いくつかのlimitationはありますが、通常のPythonコードもコンパイルできます。

cythonの使い方

cythonには二つの大きな用途があります。

  1. 既存のPythonコードを高速化する

  2. 外部のC言語のライブラリとPythonのコードを繋ぐ

cythonコードのビルドフロー

最もシンプルなCythonコードのビルドのフローは下記の通りです。

  1. cythonで書かれたコード .pyxを準備

  2. cythonで.pyxコードをコンパイルして、cコードを生成する。

  3. cコードをコンパイルして、.soか.pydにして、pythonからインポートできるようにする。

 

2と3のステップは、下記のような方法があります。

  1. setup.pyで設定する

  2. pyximportを使う

  3. cythonのコマンドラインツールでコンパイルする

  4. jupyterノートブックで、%load_ext Cythonと%%cythonマクロを使う

  例えば、setup.pyを使う場合、下記のようなsetup.pyを準備し、

## python3 setup.py build_ext --inplace

from distutils.core import setup
from Cython.Build import cythonize

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

setup.pyがあるディレクトリで、最初のコメントにあるコマンドを実行すると、

$ python3 setup.py build_ext --inplace

下記のように、Cythonで生成されたCコードと、

そのCコードを各プラットフォームに毎にコンパイルした、

soファイルファイルが生成されます。

あとは、上記のsoファイルと同じディレクトリにある

Pythonコードは

from primes_listc

で使えます。

 

pydファイル

*.pydファイルはcythonにおける、Cのヘッダーファイルのようなものです。

用途としては、

  • 外部のCコードとのインターフェイスを定義

  • インライン関数の定義

  • 複数のcythonモジュール間のデータのやり取りを定義

で利用できます。

 

例えば、下記のようにSciPyはCythonのPublic APIを公開しており、

直接、ユーザのCythonコードから、

SciPyのこれらのAPIを利用することができます。

scipy.github.io

 

Cythonの基本的な使い方

CythonはPythonのスーパーセットですので、

なんのコード変更もしなくても、コンパイルできますが、

下記のように追加で型情報を付加したり、

C/C++の機能を使ったりできます。

 

関数の引数や返値に型を付ける

下記のように、関数の引数に型を付けるには、

Cのように型情報を追加するだけです。

def sum(int x):

また返値の型も指定する場合は、

defをcdefに変えて、型情報を追加します。

cdef int sum(int x):

またinlineをつけることで、関数のインライン化も可能です。

 

変数に型を付ける

変数に型を付けるときは、cdef のあとに型情報を付けます

cdef int i
cdef int n = 2, p
cdef long sum
cdef int[:,:] counts # 二次元配列

また、cdefでブロックを作ると、毎回cdefを書かなくても良くなります。

cdef:
    int i
    int n = 2, p
    long sum

 

C/C++のvectorを使う

C言語のarrayは下記のように使えます。

cdef int[100] x

C++のstdvectorも使えます。

C++のライブラリを使う場合は、

下記のように、まず最初の行にC++を使うことを宣言し、

CythonのトランスパイラにC++コードを使うことを認識させて、

あとはvectorをインポートして使うだけです。

普通にpush_backが使えます。

# distutils: language=c++
from libcpp.vector cimport vector

cdef vector[int] vec
vec.reserve(100) # preallocation

for i in range(100)
     p_vector.push_back(i)

配列の境界チェックや負のインデックスアクセスをオフにして高速化する

pyxのコードの先頭にこのコメントを書くと、Cの配列と同様に、

配列の境界チェックや負のインデックスアクセスがオフされ、高速化されます。

# cython: boundscheck=False, wraparound=False

   

並列処理をする

Cythonを使うとOpenMPを使った並列処理も実現できます。

まず、OpenMPをCythonで使うには、このコメントを追加します。

# distutils: extra_compile_args = -fopenmp
# distutils: extra_link_args = -fopenmp

これで、関数の後ろにnogilとつけると、

GIL (Global Interpreter Lock)をオフできるようになったり、

myenigma.hatenablog.com

cython.parallelのprangeや、parallelで並列処理ができるようになります。

cython.readthedocs.io

 

Cythonの注意点

Cythonを使う場合は下記の項目に注意したほうが良いようです。

  • 必ずしもPython版より高速になるわけではない。(ちゃんとプロファイルを取る)

  • 例外は使えない (返り値で判断する)

 

参考資料

qiita.com

qiita.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com


Cython ―Cとの融合によるPythonの高速化

MyEnigma Supporters

もしこの記事が参考になり、

ブログをサポートしたいと思われた方は、

こちらからよろしくお願いします。

myenigma.hatenablog.com