ja nadal nie znalazłem całkowicie zadowalającego rozwiązania, ale mimo to jest coś można zrobić, aby uzyskać wskaźnik z dużo mniejszym obciążeniem w CPython. Po pierwsze, powód, dla którego wspomniane wyżej sposoby są tak powolne, to zarówno atrybuty na żądanie, które są ustawiane przez array_ctypes_get()
iw w numpy/numpy/core/src/multiarray/getset.c
. Pierwszy importuje ctypes i tworzy instancję numpy.core._internal._ctypes
, podczas gdy druga tworzy nowy słownik i zapełnia go dużą ilością niepotrzebnych rzeczy oprócz wskaźnika danych.
Nic nie można zrobić na poziomie Pythona o tym narzut, ale można napisać mikro modułu na poziomie C, który omija większość napowietrznej:
#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);
}
Compile jak zwykle jako rozszerzenie w setup.py
i import jako
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
na pypy, from accel import _get_ptr
zawiedzie i get_ptr
spadnie z powrotem do get_ptr_array
, który współpracuje z Numpypy.
Jeśli chodzi o wydajność, dla lekkich wywołań funkcji C, ctypes + accel._get_ptr()
jest wciąż nieco wolniejsza niż natywne rozszerzenie CPython, które zasadniczo nie ma narzutów. Jest to oczywiście znacznie szybsze niż powyżej get_ptr_ctypes()
i get_ptr_array()
, tak aby narzut mógł stać się nieistotny dla średnio aktywnych wywołań funkcji C.
Jeden uzyskał kompatybilność z PyPy, chociaż muszę powiedzieć, że po spędzeniu całkiem sporo czasu na próbach oceny PyPy dla moich aplikacji do obliczeń naukowych, nie widzę dla niej przyszłości, o ile (dość uparcie)) odmawiają wsparcia pełnego API CPython.
Aktualizacja
stwierdziliśmy, że ctypes.cast()
teraz staje się wąskim gardłem po wprowadzeniu accel._get_ptr()
. Można pozbyć się rzutów, deklarując wszystkie wskaźniki w interfejsie jako ctypes.c_void_p
. To, co skończyło się z:
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
Tutaj get_ptr_ctypes2()
unika oddanych przez dostęp do ukrytej ndarray.ctypes._data
atrybut bezpośrednio. Oto niektóre wyniki czasowe dla nazywając wagi ciężkiej i funkcje lekkich C Python:
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
Tak, z accel._get_ptr()
i nie ctypes.cast()
s, prędkość ctypes' jest rzeczywiście konkurencyjny z native rozszerzenia CPython. Więc po prostu trzeba czekać aż ktoś przepisuje h5py
, matplotlib
i scipy
z ctypes, aby móc spróbować pypy do niczego poważnego ...
Niestety, 'scipy.weave' nie robi nic innego niż generowanie kodu C przy użyciu API (CPython '#include'), który nie będzie działał z 'PyPy'. W API CPython 'PyArray_DATA()' jest najskuteczniejszym sposobem uzyskania wskaźnika do sekcji danych numpy array, ale nie jest przenośny dla PyPy. –
Stefan