私はまだ完全に満足のいく解決策を見つけていませんが、それでも、CPythonではるかに少ないオーバーヘッドでポインターを取得するためにできることがあります。まず、上記の両方の方法が非常に遅い理由は、.ctypes
と.__array_interface__
がオンデマンド属性であり、によって設定されarray_ctypes_get()
、array_interface_get()
で設定されるためnumpy/numpy/core/src/multiarray/getset.c
です。1つ目はctypesをインポートしてnumpy.core._internal._ctypes
インスタンスを作成し、2つ目は新しいディクショナリを作成してデータポインタに加えて不要なものを大量に追加します。
このオーバーヘッドについてPythonレベルでできることは何もありませんが、ほとんどのオーバーヘッドをバイパスするCレベルでマイクロモジュールを作成できます。
#include <Python.h>
#include <numpy/arrayobject.h>
PyObject *_get_ptr(PyObject *self, PyObject *obj) {
return PyLong_FromVoidPtr(PyArray_DATA(obj));
}
static PyMethodDef methods[] = {
{"_get_ptr", _get_ptr, METH_O, "Wrapper to PyArray_DATA()"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initaccel(void) {
Py_InitModule("accel", methods);
}
の拡張機能として通常どおりコンパイルし、としてsetup.py
インポートします
try:
from accel import _get_ptr
def get_ptr(x):
return C.cast(_get_ptr(x), p_t)
except ImportError:
get_ptr = get_ptr_array
PyPyでは、from accel import _get_ptr
失敗してget_ptr
にフォールバックしget_ptr_array
ます。これはNumpypyで機能します。
パフォーマンスに関する限り、軽量のC関数呼び出しのctypes + accel._get_ptr()
場合、基本的にオーバーヘッドがないネイティブのCPython拡張機能よりもかなり低速です。もちろん、これよりもはるかに高速でget_ptr_ctypes()
あるget_ptr_array()
ため、中程度の重みのC関数呼び出しではオーバーヘッドがわずかになる可能性があります。
PyPyとの互換性が得られましたが、科学計算アプリケーションでPyPyを評価するためにかなりの時間を費やした後、彼らが(かなり頑固に)サポートを拒否する限り、PyPyの将来は見えません。完全なCPythonAPI。
アップデート
ctypes.cast()
を導入した後、それがボトルネックになっていることがわかりましたaccel._get_ptr()
。インターフェイス内のすべてのポインタをとして宣言することで、キャストを取り除くことができますctypes.c_void_p
。これは私が最終的に得たものです:
def get_ptr_ctypes2(x):
return x.ctypes._data
def get_ptr_array(x):
return x.__array_interface__['data'][0]
try:
from accel import _get_ptr as get_ptr
except ImportError:
get_ptr = get_ptr_array
ここでget_ptr_ctypes2()
は、hiddenndarray.ctypes._data
属性に直接アクセスしてキャストを回避します。PythonからヘビーウェイトとライトウェイトのC関数を呼び出すタイミングの結果は次のとおりです。
heavy C (few calls) light C (many calls)
ctypes + get_ptr_ctypes(): 0.71 s 15.40 s
ctypes + get_ptr_ctypes2(): 0.68 s 13.30 s
ctypes + get_ptr_array(): 0.65 s 11.50 s
ctypes + accel._get_ptr(): 0.63 s 9.47 s
native CPython: 0.62 s 8.54 s
Cython (no decorators): 0.64 s 9.96 s
したがって、 sがある場合accel._get_ptr()
とないctypes.cast()
場合、ctypesの速度は実際にはネイティブのCPython拡張機能と競合します。だから私は誰かが書き直すまで待つ必要がありますh5py
、matplotlib
そしてscipy
ctypesで何か深刻なことのためにPyPyを試すことができるようになります...