2013-01-02 31 views
8

Próbuję skompilować ten rodzaj kodu:Jak zadeklarować listę 2D w Cython

def my_func(double c, int m): 
    cdef double f[m][m] 

    f = [[c for x in range(m)] for y in range(m)] 
    ... 

który podnosi:

Error compiling Cython file: 
------------------------------------------------------------ 
def grow(double alpha, double beta, double gamma, int m, int s): 
    cdef double f[m][m] 
        ^
------------------------------------------------------------ 
test.pyx:6:22: Not allowed in a constant expression 

po czym zakładam, że nie można używać zmiennej u spiczasty miejsce i staram się z wartości liczbowej:

def my_func(double c, int m): 
    cdef double f[500][500] 

    f = [[c for x in range(500)] for y in range(500)] 
    ... 

ale potem dostać:

Error compiling Cython file: 
------------------------------------------------------------ 
    f = [[beta for x in range(500)] for y in range(500)] 
    ^
------------------------------------------------------------ 
test.pyx:13:6: Assignment to non-lvalue 'f' 

Zastanawiam się, jak zadeklarować i utworzyć listę 2D w kodzie cythonowym. Nie mogłem znaleźć tego rodzaju przykład w dokumentacji googlowania na „liście 2D Cython”

+0

Cóż, jeśli wyjdę z deklaracji, otrzymam skompilowany kod, więc domyślam się, że moja deklaracja jest błędna. – theta

+0

Czy rzeczywiście chcesz listę list lub tablicę 2d C? – delnan

+0

Tak, to jest tak, jak jest napisane. Próbuję przyspieszyć bardzo powolny kod w języku Python, który pętli nad każdym elementem tej (i jeszcze dwóch) list. Wyobraź sobie, jak powolne. – theta

Odpowiedz

5
cdef double f[500][500] 

Ten deklaruje tablicę tablic C 500 C 500 deblu. To 500 * 500 spakowanych podwójnych wartości (przechowywanych na stosie w tym przypadku, chyba że Cython robi coś fajnego) bez żadnego pośrednictwa, co wspomaga wydajność i wykorzystanie pamięci podręcznej, ale oczywiście dodaje poważne ograniczenia. Może chcesz tego, ale powinieneś nauczyć się wystarczająco dużo C, aby wiedzieć, co to znaczy pierwszy. Nawiasem mówiąc, jednym ograniczeniem jest to, że rozmiar musi być stałą czasu kompilacji (w zależności od wersji C, C99 i C10 pozwalają na to), o czym jest pierwszy komunikat o błędzie.

Jeśli użyjesz tablic, nie zainicjujesz f tak, jak to zrobiłeś, ponieważ nie ma to żadnego sensu. f jest już podwójną zmienną 500x500, a tablice jako całość nie mogą być przypisane (co właśnie próbuje ci przekazać ten komunikat o błędzie). W szczególności, rozumienie listowe tworzy w pełni rozwinięty obiekt listy Pythona (który możesz również użyć z Cython, patrz poniżej) zawierający w pełni rozwinięte obiekty "Python" (w tym przypadku obiekty "Python" (float). Lista nie jest zgodna z tablicą C. Użyj zagnieżdżonej pętli for z przypisaniem elementu do inicjowania. Wreszcie taka tablica zajmuje 500 * 500 * 8 bajtów, czyli prawie 2 MiB. W niektórych systemach jest to większy niż cały stos, a na wszystkich innych systemach jest to tak duża część stosu, że jest to zły pomysł. Powinieneś posortować-alokować tę tablicę.

Jeśli korzystasz z listy Pythona, pamiętaj, że nie uzyskasz znacznej poprawy wydajności i wykorzystania pamięci (zakładając, że twój kod będzie głównie manipulował tą listą), ale w zamian możesz odzyskać trochę wygody. Możesz po prostu zostawić cdef lub użyć list jako typu (object również powinno działać, ale nic z tego nie zyskasz, więc równie dobrze możesz to pominąć).

Tablica NumPy może być szybsza, bardziej wydajna pod względem pamięci, bardziej wygodna w użyciu, i. Jeśli możesz zaimplementować krytyczne pod względem wydajności części swojego algorytmu w kategoriach operacji NumPy, możesz uzyskać pożądane przyspieszenie bez używania Cython na wszystkich.

+0

Dziękuję za wyjaśnienia. Sądzę, że powinienem szukać książki C z boku, a ta sama rada płynie po prostu przeglądając dokumentację Cythona. Więc dopóki nie dowiem się więcej o typach C i operacjach, po prostu użyję listy w tym przykładzie. Tablice C powinny być bardziej wydajne, jak zakładam, ale potem nie mogą być zwrócone z funkcji Cythona do kodu Pythona, ale po prostu użyć wewnątrz kodu Cythona? Przyspieszenie to na razie tylko 3x, ale postaram się pójść dalej, najpierw z Numpy i Cython, ponieważ używanie tylko tablic Numpy zamiast list nie poprawia wydajności tego przykładowego kodu, jeśli jest to wiara. – theta

8

Nie używaj rozumienia list w Cython. Nie ma przyspieszeń, ponieważ tworzą one regularną listę Pythona. Wiki says, że należy użyć dynamicznej alokacji w Cython następująco:

from libc.stdlib cimport malloc, free 

def my_func(double c, int m): 
    cdef int x 
    cdef int y 
    cdef double *my_array = <double *>malloc(m * m * sizeof(double)) 

    try: 

     for y in range(m): 
      for x in range(m): 
       #Row major array access 
       my_array[ x + y * m ] = c 

     #do some thing with my_array 

    finally: 
     free(my_array) 

Ale jeśli chcesz mieć obiekt Pythona z tablicy 2D, jego zaleca się stosowanie NumPy.

+0

Dziękuję za twój fragment, ale po prostu boję się, gdy widzę "malloc" i podobne terminy. Jeszcze mnie nie ma i łatwiej mi używać f2py, ale chciałem zacząć od Cythona przez długi czas. Jak już odpowiedziałem, spróbuję teraz z tablicami Cython i Numpy zamiast list Cython i Python. Dzięki – theta