Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Introduction to cython

14.319 Aufrufe

Veröffentlicht am

PyCon APAC 2013発表資料

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

Veröffentlicht in: Technologie
  • Loggen Sie sich ein, um Kommentare anzuzeigen.

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

×