Top Banner
Cython による 拡張モジュール 開発 2013/9/14 PyCon APAC 2013 Atsuo Ishimoto
39

Introduction to cython

Jul 02, 2015

Download

Technology

Atsuo Ishimoto

PyCon APAC 2013発表資料

プログラミング言語 Cythonの紹介
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Introduction to cython

Cythonによる

拡張モジュール開発

2013/9/14 PyCon APAC 2013 Atsuo Ishimoto

Page 2: Introduction to cython

自己紹介

2

いしもと

石本 敦夫 あつお

p 書籍「パーフェクトPython」 著者の一員

p http://www.gembook.org p python.jp ドメインの管理者 p @atsuoishimoto

Page 3: Introduction to cython

Pythonの拡張モジュール

3

p 通常、C/C++で開発し、Pythonスクリプトから利用できる関数・データ型を提供

p Pythonが動的にロードする共有ライブラリ(*.so, *.pyd)

p 用途 l  Pythonからは呼び出せないCライブラリへ

のインターフェース l  Pythonではパフォーマンス不足

Page 4: Introduction to cython

拡張モジュールのめんどくささ

4

C/C++が必要

/* 一般的なC言語の例 */ int  i;main(){for(;i["]<i;++i){-­‐-­‐i;}"];read('-­‐'-­‐'-­‐',i+++"hell\  o,  world!\n",'/'/'/'));}read(j,i,p){write(j/p+p,i-­‐-­‐-­‐j,i/i);}    

The International Obfuscated C Code Contest http://www.ioccc.org/1984/anonymous.c

Page 5: Introduction to cython

拡張モジュールのめんどくささ

5

関数・データ型定義がめんどう

static PyMemberDef xx_memberlist[] = { ... static PyGetSetDef xx_getsetlist[] = { ... static PyTypeObject xx_Type = { ... static PyMethodDef xx_methods[] = { ... static PyModuleDef xx_module = { ... PyMODINIT_FUNC PyInit_xx(void) { ...

Page 6: Introduction to cython

拡張モジュールのめんどくささ

6

参照カウント管理

p Pythonのオブジェクトは、オブジェクトの被参照数を正確にカウントする必要がある

p PyObject_SetItem()、PyList_SetItem()、 PyList_SET_ITEM()  の違いを覚えてますか?

p 間違えればメモリリークかコアダンプ

Page 7: Introduction to cython

Cythonとは

7

Python専用プログラミング言語

p Pythonの拡張モジュールを開発するための専用プログラミング言語

p Pythonのほぼ上位互換 p インタープリタではなくコンパイラ p Python2/3対応 p http://www.cython.org

Page 8: Introduction to cython

Cythonの起源

8

Pyrex

Cython

Fork

最終リリースは 2010/4/12

2002/4/3 version 0.1 リリース

Page 9: Introduction to cython

Cythonを採用したプロジェクト

9

p lxml http://lxml.de/

p Sage http://www.sagemath.org/

p SciPy http://www.scipy.org/

p PyYAML https://bitbucket.org/xi/pyyaml

Page 10: Introduction to cython

Pythonの構文で C言語と同じ処理を書ける

10

/*  C言語 */    void  spam()  {      void  *p  =  malloc(100);      if  (!p)  {          return;      }      if  (!ham())  {          goto  exit;      }      egg();  exit:      free(p);  }  

#  Cython    def  spam():      cdef  void  *p  =  malloc(100)      if  p:              try:                      if  not  ham():                              return                      egg()              finally:                      free(p)  

Page 11: Introduction to cython

Cythonの文法

11

基本は

Python2 とほぼ同じ!

def  qsort(L):     if  len(L)  <=  1:     return  L       return  (                  qsort([lt  for  lt  in  L[1:]  if  lt  <  L[0]])       +  [L[0]]       +  qsort(     [ge  for  ge  in  L[1:]  if  ge  >=  L[0]]))

http://code.activestate.com/recipes/66473-just-for-fun-quicksort-in-3-lines/

Page 12: Introduction to cython

Cythonの関数・型定義

12

かんたん

cdef  class  Spam:          cdef  double  attr1          cdef  public  double  public_attr          cdef  readonly  double  public_attr2                    def  ham(self):                  return  self.attr1  

Page 13: Introduction to cython

アーリーバインディング

13

変数の型宣言

も できる!

def  spam(dict  d):          return  len(d)  

型チェックは静的・動的両方

def  spam(dict  d):      cdef  list  L       L  =  d     #  コンパイルエラーに     #  ならない  

Page 14: Introduction to cython

Cythonコードの最適化

14

cdef  int  i  for  i  in  range(100):          …  

cdef  int  i  =  0  while  i  <  100:          …          i  +=  1  

Page 15: Introduction to cython

オブジェクトアクセスの最適化

15

Cythonコード 生成されるCコード

型宣言なし item = obj[n]  

item = PyObject_GetItem( obj, n)  

型宣言あり

cdef tuple obj cdef int n item = obj[n]  

item = PyTuple_GET_ITEM( obj, n)  

Page 16: Introduction to cython

C/C++ライブラリの利用

16

#  Python.h の PyMem_Malloc() を宣言  cdef  extern  from  "Python.h"          void*  PyMem_Malloc(size_t  n)    def  spam():          cdef  void  *p          p  =  PyMem_Malloc(100)          if  not  p:                  raise  MemoryError()  

ヘッダファイルから関数や構造体をインクルード

Page 17: Introduction to cython

定義済みライブラリ

17

標準Cランタイム関数などは定義済み

from  libc.math  cimport  sin      def  std_sin(x):          return  sin(x*x)  

p 関数・構造体・定数などをCythonで定義してある

p cimport文でインポートするだけで利用可能

Page 18: Introduction to cython

定義済みライブラリ(抜粋)

18

種類 モジュール名

Python API cpython.object PyObject_XXXの定義

python.dict PyDict_XXXの定義

C標準ライブラリ libc.stdio stdio.hで定義された関数

libc.stdlib stdlib.hで定義された関数

C++標準ライブラリ libcpp.list std::listの定義

libcpp.string std::string の定義

numpy numpy numpy API の定義

OpenMP openmp OpenMP API の定義

Posix標準ライブラリ posix.fcntl fcntl.hで定義された関数

Page 19: Introduction to cython

C/C++のデータ型

19

cdef キーワードで変数宣言

def  spam():  

       cdef  double  value          value  =  100.0  *  200          return  value  

ポインタや配列も

def  spam(s):  

       cdef  char  p,*q          p  =  s[0]          q  =  &p          return  q[0]      #  *qは不可  

Page 20: Introduction to cython

自動型変換

20

def  spam(n):          cdef  int  number          number  =  n    

p Pythonオブジェクトを、Cのint型の値に変換

p 変換不能な場合は例外を送出

int  number  =  PyInt_AS_LONG(n)  

Page 21: Introduction to cython

文字列の自動変換

21

def  spam(L):          cdef  char  *s  =  "ham"          L.append(s)  

p 文字列 s を Pythonのstrオブジェクト(Python3ではbytes)に変換

p strオブジェクト -> char * も変換される

Page 22: Introduction to cython

GIL制御

22

言語としてGILをサポート

p GIL: Global Interpreter Lock

p Pythonスクリプトが、複数のスレッドで同時に実行されないように制御する仕組み

p PythonのC APIを使わない処理の間は、GILを開放すると並列処理の効率が向上するケースも

with  nogil:        #GILを開放し、他のスレッド        #でPython実行を許可する        f  =  fopen(fname,"w")        …  

Page 23: Introduction to cython

C++サポート

23

from  collections  import  defaultdict      def  freq(values):          #  要素に、同じ値が何個あるか          #  数え上げる       d  =  defaultdict(int)     for  v  in  values:     d[v]  +=  1  

#  distutils:  language  =  c++    from  libcpp.map  cimport  map      def  freq(list  values):          #  std::map を使用  

       cdef  map[int,  int]  d            cdef  int  v              for  v  in  values:                  d[v]  +=  1  

Distutils:で、C++ファイルの生成を指示

Page 24: Introduction to cython

Cythonのビルド

24

Cythonソースファイル (*.pyx *.pyd *.pxi) cythonコマンド

Cソースファイル (*.c *.cpp)

Cコンパイラ・リンカ

Python拡張モジュール (*.so *.pyd)

Page 25: Introduction to cython

Distutilsでビルド

25

from  distutils.core  import  setup  from  Cython.Build  import  cythonize      setup(      name  =  "hello",      ext_modules  =  cythonize(                                        'hello.pyx'))  

通常の拡張モジュールと同じく、setup.py

を作成

setup.pyで ビルド・インストール

$  python  setup.py  build_ext  $  python  setup.py  install  

Page 26: Introduction to cython

対話コンソールでビルド

26

pyximport.install()で、pyxファイルを自動的にビルドしてイ

ンポート

#  hello.pyxをコンパイルし、  #  拡張モジュールをインポートする  

>>>  import  pyximport  >>>  pyximport.install()  (None,pyximport.  …)  

>>>  import  hello      

(コンパイル・リンクオプションを指定する場合には使えない)

Page 27: Introduction to cython

CythonはPythonより速い?

27

Pythonはインタープリタだから遅い

Cythonはコンパイルして実行するから速い

Page 28: Introduction to cython

ベンチマーク

28

def  newton(n):      guess  =  n/2      better  =  (guess  +  n/guess)/2      while  better  !=  guess:          guess  =  better          better  =  (guess  +  n/guess)/2      return  guess  

Page 29: Introduction to cython

パフォーマンス比較

29

$ python -m timeit -c 'import pyx_newton;pyx_newton.newton(10.**100)'

10000 loops, best of 3: 21.7 usec per loop

$ python -m timeit -c 'import py_newton;py_newton.newton(10.**100)'

10000 loops, best of 3: 36.3 usec per loop

Python版

Cython版

(Python3.3.1/Cython 0.19.1)

Page 30: Introduction to cython

Cython化だけでは速くならない

30

Pythonインタープリタは優秀

p  バイトコードインタープリタのオーバヘッドは確かにあるが…

Pythonオブジェクトの、動的な比較・演算APIが問題

p  PyNumber_TrueDivide、 PyObject_RichCompareなど

p  CythonもPythonも、同じAPIを使って演算を行うので、大きな差は出ない

Page 31: Introduction to cython

Cのデータ型で演算を行う

31

def  newton(double  n):      cdef  double  guess,  better        guess  =  n/2      better  =  (guess  +  n/guess)/2      while  better  !=  guess:          guess  =  better          better  =  (guess  +  n/guess)/2      return  guess  

Page 32: Introduction to cython

32

21.7 usec

36.3 usec Python版

Cython版

Cython(型指定)版

100000 loops, best of 3: 2.89 usec per loop

•  Cython版では、0除算でZeroDivisionError例外を送出するなどの処理があるため、Pure C版より若干遅い

Page 33: Introduction to cython

関数の呼び出しコスト

33

def  tak(x,  y,  z):          if  x  <=  y:                  return  z          return  tak(     tak(x-­‐1,  y,  z),       tak(y-­‐1,  z,  x),       tak(z-­‐1,  x,  y))  

Page 34: Introduction to cython

34

Python版

$ python -m timeit -s "import tak" "tak.tak(18, 9, 0)"

10 loops, best of 3: 2.74 sec per loop

Cython版

$ python -m timeit -s "import tak" "tak.tak(18, 9, 0)"

10 loops, best of 3: 1.47 sec per loop

Page 35: Introduction to cython

処理時間はほとんど関数呼び出し

35

Pythonの関数オブジェクトは重たい

p 引数の動的な受け渡し p フレームオブジェクトの作成

Page 36: Introduction to cython

Cの関数を定義

36

cdef  int  c_tak(int  x,  int  y,  int  z):          if  x  <=  y:                  return  z          return  c_tak(     c_tak(x-­‐1,  y,  z),       c_tak(y-­‐1,  z,  x),       c_tak(z-­‐1,  x,  y))    def  tak(x,  y,  z):          return  c_tak(x,  y,  z)  

Page 37: Introduction to cython

37

Python版

$ python -m timeit -s "import tak" "tak.tak(18, 9, 0)"

10 loops, best of 3: 2.74 sec per loop

Cython版

$ python -m timeit -s "import tak" "tak.tak(18, 9, 0)"

10 loops, best of 3: 1.47 sec per loop

Cython(cdef)版

$ python -m timeit -s "import tak" "tak.tak(18, 9, 0)"

10 loops, best of 3: 36.9 msec per loop

Page 38: Introduction to cython

C言語の関数

38

できるだけC/C++の関数を呼び出す

p 呼び出しコスト:低 p インライン化も可能 p Pythonからは呼び出せない

Page 39: Introduction to cython

ご清聴ありがとうございました

39