2013-11-15 12 views
12

Per esempio, se faccio questo:Come viene gestita la memoria per np.ndarray in cython?

cdef np.ndarray[np.int64_t, ndim=1] my_array 

Dov'è la mia my_array memorizzato? Penserei che siccome non ho detto a cython di archiviarlo nell'heap, esso sarebbe stato memorizzato nello stack, ma dopo aver fatto il seguente esperimento sembra che sia archiviato nello heap, o in qualche modo efficientemente gestito dalla memoria. Come viene gestita la memoria rispetto a my_array? Forse mi manca qualcosa di ovvio, ma non ho trovato alcuna documentazione su di esso.

import numpy as np 
cimport cython 
cimport numpy as np 

from libc.stdlib cimport malloc, free 

def big_sum(): 
    # freezes up: 
    # "a" is created on the stack 
    # space on the stack is limited, so it runs out 

    cdef int a[10000000] 

    for i in range(10000000): 
     a[i] = i 

    cdef int my_sum 
    my_sum = 0 
    for i in range(10000000): 
     my_sum += a[i] 
    return my_sum 

def big_sum_malloc(): 
    # runs fine: 
    # "a" is stored on the heap, no problem 

    cdef int *a 
    a = <int *>malloc(10000000*cython.sizeof(int)) 

    for i in range(10000000): 
     a[i] = i 

    cdef int my_sum 
    my_sum = 0 
    for i in range(10000000): 
     my_sum += a[i] 

    with nogil: 
     free(a) 
    return my_sum  

def big_numpy_array_sum(): 
    # runs fine: 
    # I don't know what is going on here 
    # but given that the following code runs fine, 
    # it seems that entire array is NOT stored on the stack 

    cdef np.ndarray[np.int64_t, ndim=1] my_array 
    my_array = np.zeros(10000000, dtype=np.int64) 

    for i in range(10000000): 
     my_array[i] = i 

    cdef int my_sum 
    my_sum = 0 
    for i in range(10000000): 
     my_sum += my_array[i] 
    return my_sum 
+4

Perché non dai un'occhiata al file C generato? Comunque credo che cython chiami semplicemente funzioni numpy per l'allocazione, che chiamano 'PyMalloc' che alloca sull'heap. numpy * non * gestisce la sua memoria. Si basa semplicemente su allocazioni/deallocazioni python. – Bakuriu

+1

@Bakuriu, grazie per il tuo commento, ha senso e aiuta molto, ma sai di una fonte che spiega questi passaggi in modo più dettagliato? Ho provato a guardare il file C generato, ma sono oltre 6000 linee di codice, e non ho potuto avere molto senso. – Akavall

+0

È quasi certamente heap - considera che la dimensione della matrice non è nota al momento della dichiarazione, numpy di solito funziona su array di grandi dimensioni e lo stack è limitato. Sebbene l'ottimizzazione dello stack sia tecnicamente possibile, 'ndarray's può essere vista, quindi il riferimento ai dati può sfuggire allo scope corrente. In quanto tale, è molto più semplice implementarlo in heap. Utilizzare un MemoryView se possibile, o leggere il http://docs.cython.org/src/tutorial/numpy.html –

risposta

1

Cython non sta facendo nulla di magico qui. Numpy ha un C-api completo, ed è ciò con cui Cython sta interagendo - cython non sta eseguendo la gestione della memoria stessa, e la memoria nell'array numpy viene gestita nello stesso modo in cui lo è quando si usa un array numpy da python. @ Bakuriu ha ragione - questo è decisamente in pericolo.

Considerate questo codice Cython:

cimport numpy as np 
def main(): 
    zeros = np.zeros 
    cdef np.ndarray[dtype=np.double_t, ndim=1] array 
    array = zeros(10000) 

Questo si traduce al seguente C in funzione principale equivalente. Ho rimosso le dichiarazioni e il codice di gestione degli errori per renderlo più pulito da leggere.

PyArrayObject *__pyx_v_array = 0; 
PyObject *__pyx_v_zeros = NULL; 
PyObject *__pyx_t_1 = NULL; 
PyObject *__pyx_t_2 = NULL; 

// zeros = np.zeros    # <<<<<<<<<<<<<< 
// get the numpy module object 
__pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); 
// get the "zeros" function 
__pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__zeros) 
__pyx_v_zeros = __pyx_t_2; 

// array = zeros(10000)    # <<<<<<<<<<<<<< 
// (__pyx_k_tuple_1 is a static global variable containing the literal python tuple 
// (10000,) that was initialized during the __Pyx_InitCachedConstants function) 
__pyx_t_2 = PyObject_Call(__pyx_v_zeros, ((PyObject *)__pyx_k_tuple_1), NULL); 
__pyx_v_array = ((PyArrayObject *)__pyx_t_2); 

Se si guarda la documentazione NumPy C api, vedrai che è PyArrayObject struct C-API della NumPy del ndarray. Il punto chiave qui è vedere che Cython non gestisce affatto l'allocazione di memoria. Gli stessi principi di progettazione orientati agli oggetti si applicano alle apis C python e numpy e la gestione della memoria qui è responsabilità di PyArrayObject. La situazione non è diversa dall'uso di una matrice numpy in python.

Problemi correlati