CFFIを使用してnumpy配列をCコードに渡す関数を記述しました。これは、バッファプロトコルとメモリビューを使用して、データをコピーせずに効率的に渡します。しかし、これは、C連続配列を渡し、正しい型を使用することを保証する必要があることを意味します。 Numpyはこれを行う関数numpy.ascontiguous,
を提供します。だから私は議論を繰り返し、この関数を適用します。以下の実装は機能し、一般的な関心事である可能性があります。しかし、それが呼び出される回数があれば、それは遅いです。 (それをスピードアップする方法に関する一般的なコメントは役に立ちます)ジェネレーターの理解とリストの理解は異なる方法で反復する
しかし、実際の質問は、最初のリストの理解をジェネレータの理解に置き換えた場合、またはコードをリファクタリングしてnp.ascontigous
がもう1つは、Cコードに渡されたポインタがnumpy配列の先頭を指していないことです。私はそれが呼ばれていないと思う。私は理解を繰り返し、戻り値のみを使用しています。なぜリストの理解やジェネレーターの理解が何を変えるのでしょうか?
def cffi_wrap(cffi_func, ndarray_params, pod_params, return_shapes=None):
"""
Wraps a cffi function to allow it to be called on numpy arrays.
It uss the numpy buffer protocol and and the cffi buffer protocol to pass the
numpy array into the c function without copying any of the parameters.
You will need to pass dimensions into the C function, which you can do using
the pod_params.
Parameters
----------
cffi_func : c function
This is a c function declared using cffi. It must take double pointers and
plain old data types. The arguments must be in the form of numpy arrays,
plain old data types, and then the returned numpy arrays.
ndarray_params : iterable of ndarrays
The numpy arrays to pass into the function.
pod_params : tuple of plain old data
This plain old data objects to pass in. This may include for example
dimensions.
return_shapes : iterable of tuples of positive ints
The shapes of the returned objects.
Returns
-------
return_vals : ndarrays of doubles.
The objects to be calculated by the cffi_func.
"""
arr_param_buffers = [np.ascontiguousarray(param, np.float64)
if np.issubdtype(param.dtype, np.float)
else np.ascontiguousarray(param, np.intc) for param in ndarray_params]
arr_param_ptrs = [ffi.cast("double *", ffi.from_buffer(memoryview(param)))
if np.issubdtype(param.dtype, np.float)
else ffi.cast("int *", ffi.from_buffer(memoryview(param)))
for param in arr_param_buffers]
if return_shapes is not None:
return_vals_ptrs = tuple(ffi.new("double[" + str(np.prod(shape)) + "]")
for shape in return_shapes)
returned_val = cffi_func(*arr_param_ptrs, *pod_params, *return_vals_ptrs)
return_vals = tuple(np.frombuffer(ffi.buffer(
return_val))[:np.prod(shape)].reshape(shape)
for shape, return_val in zip(return_shapes, return_vals_ptrs))
else:
returned_val = cffi_func(*arr_param_ptrs, *pod_params)
return_vals = None
if returned_val is not None and return_vals is not None:
return_vals = return_vals + (returned_val,)
elif return_vals is None:
return_vals = (returned_val,)
if len(return_vals) == 1:
return return_vals[0]
else:
return return_vals
バッファプロトコルに依存して1回のコピーを避けることはできますが、 'np.ascontiguousarray()'がデータをコピーしないと考える特別な理由はありません。それがコードが遅い理由の1つになる可能性があります。修飾されていない配列オブジェクトをそのまま渡し、NumpyのC APIを使ってC側でアクセスすると、より良い結果が得られるかもしれません。私はcffiでうまくいきませんが、それは何かを考慮する必要があります。 –
私はそれが可能だと思います。私は間違っていると思いますが、np.ascontiguousはコピーが必要な場合にのみコピーすると思います。ほとんどの場合、何もするべきではありません。コードをプロファイルする場合、遅い部分はnp.issubdtype()とキャストです。キャスティング、私は本当に離れて得ることはできません。問題は、np.issubdtype()を2回呼び出す必要がないという事実から来ています。 – sangrey
"それは動作を停止" ---より正確にすることはできますか?どのように失敗するのですか? –