2016-05-24 8 views
2

Ich überschreibe etwas von meinem Python-Code mit Cython.Das Deklarieren eines numpy-Arrays mit Cython erzeugt merkwürdigerweise eine Menge Overhead

Nach den Vorschlägen in the documentation begann ich meine Python-Arrays mit der optimierten Cython-Definition zu ersetzen.

Insbesondere ist die nach dem ‚besten‘ Weg der Vereinbarkeit eines numpy Array sein soll:

# cython: profile=True 
# cython: boundscheck=False 
# cython: wraparound=False 

import numpy as np 
cimport numpy as np 

cpdef test(): 

    cdef np.ndarray[np.int_t, ndim=1] seeds_idx = np.empty(10, dtype=np.int) 

    pass 

erzeugt die HTML-Datei über cython -a my_file.pyx den Code jedoch oben durch Profilieren zeigt folgendes:

+10:  cdef np.ndarray[np.int_t, ndim=1] seeds_idx = np.empty(10, dtype=np.int) 
    __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_t_1); 
    __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_empty); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_t_2); 
    __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; 
    __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_t_1); 
    __pyx_t_3 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_t_3); 
    __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s_int); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_t_4); 
    __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; 
    if (PyDict_SetItem(__pyx_t_1, __pyx_n_s_dtype, __pyx_t_4) < 0) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; 
    __pyx_t_4 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_tuple_, __pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_t_4); 
    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; 
    __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; 
    if (!(likely(((__pyx_t_4) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_4, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 10, __pyx_L1_error) 
    __pyx_t_5 = ((PyArrayObject *)__pyx_t_4); 
    { 
    __Pyx_BufFmt_StackElem __pyx_stack[1]; 
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_seeds_idx.rcbuffer->pybuffer, (PyObject*)__pyx_t_5, &__Pyx_TypeInfo_nn___pyx_t_5numpy_int_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) { 
     __pyx_v_seeds_idx = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_seeds_idx.rcbuffer->pybuffer.buf = NULL; 
     __PYX_ERR(0, 10, __pyx_L1_error) 
    } else {__pyx_pybuffernd_seeds_idx.diminfo[0].strides = __pyx_pybuffernd_seeds_idx.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_seeds_idx.diminfo[0].shape = __pyx_pybuffernd_seeds_idx.rcbuffer->pybuffer.shape[0]; 
    } 
    } 
    __pyx_t_5 = 0; 
    __pyx_v_seeds_idx = ((PyArrayObject *)__pyx_t_4); 
    __pyx_t_4 = 0; 
/* … */ 
    __pyx_tuple_ = PyTuple_Pack(1, __pyx_int_10); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_tuple_); 
    __Pyx_GIVEREF(__pyx_tuple_); 

Dies wurde auf Python 2.7 mit Cython 0.24 und numpy 1.10.4 erhalten.

Auf der anderen Seite, die sehr einfache Erklärung seeds_idx = np.empty(10) Ergebnisse in:

+10:  seeds_idx = np.empty(10) 
    __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_t_1); 
    __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_empty); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_t_2); 
    __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; 
    __pyx_t_1 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_tuple_, NULL); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_t_1); 
    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; 
    __pyx_v_seeds_idx = __pyx_t_1; 
    __pyx_t_1 = 0; 
/* … */ 
    __pyx_tuple_ = PyTuple_Pack(1, __pyx_int_10); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 10, __pyx_L1_error) 
    __Pyx_GOTREF(__pyx_tuple_); 
    __Pyx_GIVEREF(__pyx_tuple_); 

Was hier schief läuft (falls vorhanden)? Vielen Dank!

+1

Es ist nichts falsch, numpy Arrays sind komplexe (aber sehr effiziente) Datenstrukturen. Sie können stattdessen versuchen, [typed memoryviews] (http://docs.cython.org/src/userguide/memoryviews.html) zu verwenden, sie sind normalerweise schneller und können leicht in numme Arrays konvertiert werden. –

+0

Der andere Punkt, der sich lohnt, ist, dass __is__ Overhead beim Zuweisen des Arrays ist. Verwenden Sie das Array von schnell, aber die Zuordnung kann ein wenig langsamer sein, also versuchen Sie es nicht unnötig. – DavidW

+0

Ich sehe, so gibt es einen kleinen Aufwand während der Deklaration, aber eine viel schnellere Zuordnung/Zugriff/etc. – Gioker

Antwort

1

Wie der Kommentar sagt, ist hier nichts falsch, also brauchen Sie sich keine Sorgen zu machen. Denken Sie auch daran, dass Sie den für eine einfache Zuweisung generierten Code überprüfen, da sich Unterschiede nicht auf die Leistung auswirken.

Eine kleine Errata, im zweiten Fall seeds_idx = np.empty(10) sollte jedoch geändert werden, um seeds_idx = np.empty(10, dtype=np.int) mit dem ersten übereinstimmen.

Wenn Sie hinzufügen, dass dann das Wörterbuch, das zum Speichern der Argumente des Funktionsaufrufs (np.empty) wird ebenfalls hinzugefügt erstellt:

__pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 8, __pyx_L1_error) 
__Pyx_GOTREF(__pyx_t_1); 

die Suche für np.int:

__pyx_t_3 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 10, __pyx_L1_error) 
__Pyx_GOTREF(__pyx_t_3); 
__pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s_int); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 10, __pyx_L1_error) 
__Pyx_GOTREF(__pyx_t_4); 
__Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; 

und Die Einstellung der Argumente im neu erstellten Wörterbuch ist erfolgt:

if (PyDict_SetItem(__pyx_t_1, __pyx_n_s_dtype, __pyx_t_4) < 0) __PYX_ERR(0, 8, __pyx_L1_error) 
__Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; 

Anders als diese, ist der einzige Unterschied zwischen ihnen die folgenden:

if (!(likely(((__pyx_t_4) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_4, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 10, __pyx_L1_error) 
    __pyx_t_5 = ((PyArrayObject *)__pyx_t_4); 
    { 
    __Pyx_BufFmt_StackElem __pyx_stack[1]; 
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_seeds_idx.rcbuffer->pybuffer, (PyObject*)__pyx_t_5, &__Pyx_TypeInfo_nn___pyx_t_5numpy_int_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) { 
     __pyx_v_seeds_idx = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_seeds_idx.rcbuffer->pybuffer.buf = NULL; 
     __PYX_ERR(0, 10, __pyx_L1_error) 
    } else {__pyx_pybuffernd_seeds_idx.diminfo[0].strides = __pyx_pybuffernd_seeds_idx.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_seeds_idx.diminfo[0].shape = __pyx_pybuffernd_seeds_idx.rcbuffer->pybuffer.shape[0]; 
    } 
    } 

Welche ist as stated in the documentation you linked höchstwahrscheinlich durchgeführt, um einen schnellen Zugriff auf den Datenpuffer zu haben.

Die beste Alternative ist bei weitem typed memoryviews. Dies ist der native Weg und wahrscheinlich der einfachste Weg, mit Arrays in Cython zu arbeiten. Their performance is usually on par with numpy arrays und wenn nicht, können Sie immer einfach zwischen ihnen wechseln.

+0

Dann ist meine Frage, was ist der Vorteil auf ein numpy Array auf diese Weise über ein Wörterbuch direkt zu deklarieren? – Gioker

+0

Ich war ein wenig vage bei der Wörterbucherstellung; Ein Wörterbuch wird immer dann erstellt, wenn Sie Schlüsselworte an eine Funktion übergeben wollen. Im Fall von 'np.empty (10, dtype = np.int)' wird ein Wörterbuch erstellt, in dem die Argumente gespeichert werden. Im Fall von 'np.empty (10)' wird kein Wörterbuch erstellt. Es ist immer ratsam, den Typ des Arrays zu deklarieren, damit Cython den generierten 'c' Code darauf basierend optimieren kann. Ich habe meine Antwort aktualisiert, um einige Links auf * typed memoryviews * einzufügen, die die empfohlene Vorgehensweise in Cython sind. –

+0

Oh ok, nur für die Keywords ... immer noch eine Menge Overhead, nehme ich an. Auf die Frage der getippten memoryview habe ich eine neue Frage gestellt [hier] (http://stackoverflow.com/questions/37432076/cython-typed-memoryviews-what-they-really-are). Danke Jim! – Gioker