2013-11-15 20 views
12

Na przykład, jeśli mogę to zrobić? Pomyślałbym, że skoro nie powiedziałem cythonowi, aby zapisał na stosie, byłby on przechowywany na stosie, ale po wykonaniu następującego eksperymentu wydaje się, że jest on przechowywany na stercie lub w jakiś sposób sprawnie zarządzany pamięcią. Jak zarządza się pamięcią w odniesieniu do my_array? Może brakuje mi czegoś oczywistego, ale nie mogłem znaleźć żadnej dokumentacji na ten temat.W jaki sposób pamięć jest obsługiwana dla np.ndarray w cython?</p> <pre><code>cdef np.ndarray[np.int64_t, ndim=1] my_array </code></pre> <p>Gdzie jest moja <code>my_array</code> przechowywane:

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

Dlaczego nie rzucić okiem na wygenerowany plik C? W każdym razie wierzę, że cython po prostu wywołuje funkcje numpy do alokacji, które wywołują 'PyMalloc', który alokuje na stercie. numpy ma * nie * zarządza swoją pamięcią. Opiera się jedynie na przydziałach/deallocacjach Pythona. – Bakuriu

+1

@Bururiu, dziękuję za komentarz, to ma sens i bardzo pomaga, ale czy znasz źródło, które wyjaśnia te kroki bardziej szczegółowo? Próbowałem spojrzeć na wygenerowany plik C, ale zawiera on ponad 6000 linii kodu i nie mogłem tego zrozumieć. – Akavall

+0

To prawie na pewno sterty - należy wziąć pod uwagę, że rozmiar tablicy nie jest znany w momencie deklaracji, numpy zwykle działa na dużych tablicach, a stos jest ograniczony. Chociaż optymalizacja stosu jest technicznie możliwa, 'ndarray's może być widokami, dlatego dane odniesienia mogą uciec od obecnego zakresu. W związku z tym łatwiej jest zaimplementować go w sterty. Skorzystaj z MemoryView, jeśli to możliwe, lub przeczytaj http://docs.cython.org/src/tutorial/numpy.html –

Odpowiedz

1

Cython nie robi tutaj nic magicznego. Numpy ma pełne C-api, i właśnie to oddziałuje z cytonami - cyton nie wykonuje samego zarządzania pamięcią, a pamięć w tablicy numpy jest obsługiwana w taki sam sposób, jak w przypadku korzystania z tablicy numpy z Pythona. @Bururiu ma rację - jest to zdecydowanie na kupie.

Rozważmy ten kod Cython:

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

ten zostanie przetłumaczony na poniższym C w podobnej funkcji głównego. Usunąłem deklaracje i kod obsługi błędów, aby ułatwić czytanie.

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); 

Jeśli przyjrzeć się dokumentacji numpy API C, zobaczysz, że PyArrayObject jest także NumPy ndarray C-api struct. Kluczową kwestią jest to, że cyton nie obsługuje jawnie alokacji pamięci. Te same zasady projektowania zorientowane obiektowo mają zastosowanie do pythona i numpy C apis, a zarządzanie pamięcią jest odpowiedzialne za PyArrayObject. Sytuacja nie różni się od użycia tablicy numpy w pythonie.