Cython上で、PythonでもCでも使える配列バッファオブジェクトを使いたい。どうもCythonのcython.view.array
なるものとCPythonのarray.array
なるものが存在するようだ。
Cython array
cython.view.array
を使う。
初期化
from cython.view cimport array # note that this is 'c'-import
arr = array(shape=(3,3,3), itemsize=sizeof(int), format='i')
Python floatの場合なら'd'でdoubleを使うことになるだろう。
メモリ内での配置として、numpyでのorderのように、cythonでは"mode"なるキーワードを使うことができる。
C配列として取り出す(Memoryview)
対応するデータ型の配列に直接代入することで、Cの配列(あるいはポインタ)として扱うことができるようになる(実際にはarrayに対する「ビュー」がC配列に入ることになる)。
from cython.view cimport array
arr = array(shape=(3, 10), itemsize=sizeof(double), format='d')
cdef double[:,:] carr2d = arr # 2D C-array view
cdef double[:] carr1d = arr # 1D C-array view
上のような明示的な代入だけでなく、関数の引数指定でも引き起こすことができる。
cpdef double process(double[:,:] view) nogil:
...
arr = array(shape=(3, 10), itemsize=sizeof(double), format='d')
process(arr)
Python array
Pythonの文にimport array
が、Cythonの型情報としてfrom cpython cimport array
が必要。
StackOverflow上のベンチマークでは「cython arrayよりも速い」とのことだが、実際どうなのかは知らない。
あと基本1次元配列しか扱えないようなので、Cython内で2次元アクセスしたい場合は面倒かもしれない。
初期化
今のところ、numpy.empty()
っぽく動作させるにはclone()
を用いるしかなさげ。
from cpython cimport array as carray
import array
cdef carray.array template = array.array('d')
cdef carray.array buffer = carray.clone(template, 1000, zero=False)
# このclone()関数は、cython側で定義してくれている
# 'zero'キーワードで、ゼロ初期化するかどうか指定
C配列として取り出す
data
メンバにas_xxxs
あるいは```as_voidptr``なるフィールドが定義されており、これが配列/ポインタとして機能する。
from libc.stdio cimport printf
printf("%c", buffer.data.as_chars[0]) # 最初の要素を出力
このポインタへのアクセスは型チェックがないぶん危険だけれど、GILにとらわれないので速い。
いちおうmemoryview方式の代入もできる(けれど、GILが必要)。この場合、2次元以上のビューがつくれるのか不明。
Python上で使いやすく変換する
Python文字列への変換
byte型の1次元配列(ビュー)の場合、Python文字列に変換できる。
pystring = bytearr.tobytes().decode('utf8')
# エンコーディングは適切なものを指定
NumPy ndarrayへの変換
cython.view.array
もarray.array
もバッファオブジェクトとして使えるので、numpy.asarray()
やnumpy.array()
で変換できる、らしい。
まとめ
機能的にそこまで差があるわけではない。
- 1次元になっても速さが欲しいなら
array.array
の方がよいだろう。 - Cython内で多次元配列として使いたいなら
cython.view.array
の方がよいだろう。