Cython で連続メモリ領域としてアドレスを取れる配列たち
Cython で C API との連携をする際、 連続メモリ領域を確保してアドレスを渡すような場面によく遭遇します。
libc.malloc
を使わずに*1これを実現するためには、
cdef
宣言できるような型が用意されている 必要があり、それを叶える方法がいくつかあります。
コンパイル時にサイズを決定するもの:
- C 言語風の配列
実行時にサイズを決定できるもの:
cpython.array
cython.view.array
- 型付きメモリビューと
numpy.array
C 言語風の配列
サイズは コンパイル時 に決定する必要があります。
cdef char c_buffer[10] # アドレスを取得 print(<size_t> c_buffer) # => 140734724932822 # 要素数に変数は使えない # cdef char c_buffer[dim] # => Not allowed in a constant expression
cpython.array
Cython には、Python の処理系のうち C 言語で実装されている CPython のモジュールへのインターフェイスが用意されています。
github.com/cython/Cython/Includes/cpython
このなかで、 arrayモジュールへのインターフェイス を使うことで、連続メモリ領域を cdef
宣言できます。
実行時にサイズ(dim)を決定できます。
from cpython cimport array as cpython_array dim = 10 cdef cpython_array.array cpython_buffer = cpython_array.array('b', range(dim)) # アドレスを取得 print(<size_t> cpython_buffer.data.as_voidptr) # => 4448305408
初期化の箇所の 'b'
については、 Python の array の型コード に準じます。
アドレス取得の箇所については、 data
が 各型用ポインタの union になっている ので、ここでは void*
として取り出しています。
cython.view.array
Cython の array を使います。
実行時にサイズ(dim)を決定できます。
from cython.view cimport array as cython_array dim = 10 cdef cython_array cython_buffer = cython_array(shape=(dim,), itemsize=sizeof(char), format='b') # アドレスを取得 print(<size_t> cython_buffer.data) # => 140489752843568 # [ ] の結果は Python オブジェクトのため不可 # print(<size_t>&cython_buffer[0]) # => Cannot take address of Python object
data
が char*
ポインタなので、そこからアドレスを取れます。
次の項で紹介する「型付きメモリビュー」をとることでも、アドレスを取得できます。
cdef char[:] view = cython_buffer # Typed Memoryview print(<size_t>&view[0])
型付きメモリビューと numpy.array
Numpy で領域を確保し、 Cython の 型付きメモリビュー で参照します。もちろんサイズは実行時に与えることができます。
import numpy dim = 10 np_buffer = numpy.arange(dim, dtype=numpy.dtype('b')) cdef char[:] view = np_buffer # アドレスを取得 print(<size_t>&view[0]) # => 140502025142752 # 直接アドレス取得はできない # print(<size_t>&np_buffer[0]) # => Cannot take address of Python object
参考 URL
- Typed Memoryviews - Cython ドキュメント
- cython/Cython/Includes/cpython/array.pxd - GitHub
- cython/Cython/Utility/MemoryView.pyx - GitHub
- Cythonで使える配列バッファオブジェクト - Qiita
*1:自分でメモリの寿命を管理したくないですよね。