2013-03-04 14 views
8

Niedawno wypróbowałem PyPy i byłem zaintrygowany podejściem. Mam wiele rozszerzeń C dla Pythona, które wszystkie używają PyArray_DATA() do uzyskania wskaźnika do sekcji danych macierzy numpy. Niestety, PyPy nie wydaje się wyeksportować odpowiednika dla swoich macierzy numpypy w swoim module , więc starałem się postępować zgodnie z zaleceniami na ich stronie internetowej, aby użyć ctypes. Spowoduje to przeniesienie wskaźnika na poziom Pythona.Przenośny/szybki sposób na uzyskanie wskaźnika do danych Numpy/Numpypy

Pojawiają się dwa sposoby:

import ctypes as C 
p_t = C.POINTER(C.c_double) 

def get_ptr_ctypes(x): 
    return x.ctypes.data_as(p_t) 

def get_ptr_array(x): 
    return C.cast(x.__array_interface__['data'][0], p_t) 

Tylko drugi działa na pypy, tak dla kompatybilności wybór jest jasny. Dla CPython oba są powolne jak diabli i kompletne wąskie gardło dla mojej aplikacji! Czy istnieje szybki i przenośny sposób na uzyskanie tego wskaźnika? Czy istnieje odpowiednik PyArray_DATA() dla PyPy (ewentualnie nieudokumentowane)?

Odpowiedz

4

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 ...

0

To może nie być wystarczająca odpowiedź, ale mam nadzieję, że to dobra wskazówka. Używam scipy.weave.inline() w niektórych częściach mojego kodu. Nie wiem zbyt wiele o szybkości samego interfejsu, ponieważ funkcja I execute jest dość ciężka i opiera się tylko na kilku wskaźnikach/tablicach, ale wydaje mi się szybka. Być może można zaczerpnąć inspirację z kodem scipy.weave, szczególnie od attempt_function_call

https://github.com/scipy/scipy/blob/master/scipy/weave/inline_tools.py#L390

Jeśli chcesz rzucić okiem na kod C++, który jest generowany przez scipy.weave,

  1. produkować prosty przykład stąd: http://docs.scipy.org/doc/scipy/reference/tutorial/weave.html,

  2. Uruchom skrypt python

  3. uzyskać scipy.weave folderu cache:

    import scipy.weave.catalog as ctl 
    ctl.default_dir() 
    Out[5]: '/home/user/.python27_compiled' 
    
  4. spojrzeć wygenerowanego kodu C++ w folderze
+0

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

Powiązane problemy