Introduction to cython

12,015 views
11,832 views

Published on

PyCon APAC 2013発表資料

プログラミング言語 Cythonの紹介

Published in: Technology
0 Comments
17 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
12,015
On SlideShare
0
From Embeds
0
Number of Embeds
7,543
Actions
Shares
0
Downloads
41
Comments
0
Likes
17
Embeds 0
No embeds

No notes for slide

Introduction to cython

  1. 1. Cythonによる 拡張モジュール 開発 2013/9/14 PyCon APAC 2013 Atsuo Ishimoto
  2. 2. 自己紹介 2 いしもと 石本 敦夫 あつお p 書籍「パーフェクトPython」 著者の一員 p http://www.gembook.org p python.jp ドメインの管理者 p @atsuoishimoto
  3. 3. Pythonの拡張モジュール 3 p 通常、C/C++で開発し、Pythonスクリプト から利用できる関数・データ型を提供 p Pythonが動的にロードする共有ライブラリ (*.so, *.pyd) p 用途 l  Pythonからは呼び出せないCライブラリへ のインターフェース l  Pythonではパフォーマンス不足
  4. 4. 拡張モジュールのめんどくささ 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
  5. 5. 拡張モジュールのめんどくささ 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) { ...
  6. 6. 拡張モジュールのめんどくささ 6 参照カウント管理 p Pythonのオブジェクトは、オブジェクトの被参照数 を正確にカウントする必要がある p PyObject_SetItem()、PyList_SetItem()、 PyList_SET_ITEM()  の違いを覚えてますか? p 間違えればメモリリークかコアダンプ
  7. 7. Cythonとは 7 Python専用プログラミング言語 p Pythonの拡張モジュールを開発するため の専用プログラミング言語 p Pythonのほぼ上位互換 p インタープリタではなくコンパイラ p Python2/3対応 p http://www.cython.org
  8. 8. Cythonの起源 8 Pyrex Cython Fork 最終リリースは 2010/4/12 2002/4/3 version 0.1 リリース
  9. 9. 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
  10. 10. 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)  
  11. 11. 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/
  12. 12. Cythonの関数・型定義 12 かんたん cdef  class  Spam:          cdef  double  attr1          cdef  public  double  public_attr          cdef  readonly  double  public_attr2                    def  ham(self):                  return  self.attr1  
  13. 13. アーリーバインディング 13 変数の型宣言 も できる! def  spam(dict  d):          return  len(d)   型チェックは静 的・動的両方 def  spam(dict  d):      cdef  list  L       L  =  d     #  コンパイルエラーに     #  ならない  
  14. 14. Cythonコードの最適化 14 cdef  int  i   for  i  in  range(100):          …   cdef  int  i  =  0   while  i  <  100:          …          i  +=  1  
  15. 15. オブジェクトアクセスの最適化 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)  
  16. 16. 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()   ヘッダファイルから関数や構造体をインクルード
  17. 17. 定義済みライブラリ 17 標準Cランタイム関数な どは定義済み from  libc.math  cimport  sin       def  std_sin(x):          return  sin(x*x)   p 関数・構造体・定数など をCythonで定義してあ る p cimport文でインポート するだけで利用可能
  18. 18. 定義済みライブラリ(抜粋) 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で定義された関数 …
  19. 19. 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は不可  
  20. 20. 自動型変換 20 def  spam(n):          cdef  int  number          number  =  n     p Pythonオブジェクトを、C のint型の値に変換 p 変換不能な場合は例外を 送出 int  number  =  PyInt_AS_LONG(n)  
  21. 21. 文字列の自動変換 21 def  spam(L):          cdef  char  *s  =  "ham"          L.append(s)   p 文字列 s を Pythonのstrオブジェクト (Python3ではbytes)に変換 p strオブジェクト -> char * も変換される
  22. 22. GIL制御 22 言語としてGILを サポート p GIL: Global Interpreter Lock p Pythonスクリプトが、複数のス レッドで同時に実行されないよう に制御する仕組み p PythonのC APIを使わない処理 の間は、GILを開放すると並列 処理の効率が向上するケースも with  nogil:        #GILを開放し、他のスレッド        #でPython実行を許可する        f  =  fopen(fname,"w")        …  
  23. 23. 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++ ファイルの生成を指示
  24. 24. Cythonのビルド 24 Cythonソースファイル (*.pyx *.pyd *.pxi) cythonコマンド Cソースファイル (*.c *.cpp) Cコンパイラ・リンカ Python拡張モジュール (*.so *.pyd)
  25. 25. 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  
  26. 26. 対話コンソールでビルド 26 pyximport.install() で、pyxファイルを自 動的にビルドしてイ ンポート #  hello.pyxをコンパイルし、   #  拡張モジュールをインポートする   >>>  import  pyximport   >>>  pyximport.install()   (None,pyximport.  …)   >>>  import  hello       (コンパイル・リンクオプションを指定する場合には使えない)
  27. 27. CythonはPythonより速い? 27 Pythonはインタープリ タだから遅い Cythonはコンパイル して実行するから速い
  28. 28. ベンチマーク 28 def  newton(n):      guess  =  n/2      better  =  (guess  +  n/guess)/2      while  better  !=  guess:          guess  =  better          better  =  (guess  +  n/guess)/2      return  guess  
  29. 29. パフォーマンス比較 29 $ python -m timeit -c 'import pyx_newton;pyx_newton.newton(10.**100)' 10000 loops, best of 3: 21.7 usecper 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)
  30. 30. Cython化だけでは速くならない 30 Pythonインタープ リタは優秀 p  バイトコードインタープリタ のオーバヘッドは確かにあ るが… Pythonオブジェク トの、動的な比較・ 演算APIが問題 p  PyNumber_TrueDivide、 PyObject_RichCompare など p  CythonもPythonも、同じ APIを使って演算を行うの で、大きな差は出ない
  31. 31. 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  
  32. 32. 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版より若干遅い
  33. 33. 関数の呼び出しコスト 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))  
  34. 34. 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
  35. 35. 処理時間はほとんど関数呼び出し 35 Pythonの関数オブ ジェクトは重たい p 引数の動的な受け渡し p フレームオブジェクトの作成
  36. 36. 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)  
  37. 37. 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
  38. 38. C言語の関数 38 できるだけC/C++の 関数を呼び出す p 呼び出しコスト:低 p インライン化も可能 p Pythonからは呼び出せない
  39. 39. ご清聴ありがとうございました 39

×