2017-10-11 5 views
2

Je voudrais créer une fonction Cython qui lit un tableau et retourne un tableau. Cette fonction serait appelée à partir d'autres fonctions cdef, pas de fonctions python def. Voici ce que j'ai.Cython: créer une fonction C qui retourne un tableau

Dans mon fichier .pxd:

cdef int[:] array_test(double *x) nogil 

Dans mon fichier .pyx:

cdef inline int[:] array_test(double *x) nogil: 

    cdef int output[2] 
    output[0]=1 
    output[1]=9 

    return output 

Mais quand je compile, je reçois l'erreur: "Opération non autorisée sans gil" Quelqu'un peut-il aidez s'il vous plaît?

Répondre

3

Il y a probablement un malentendu: cette fonction ne renvoie pas un tableau C mais une tranche de mémoire. Vous ne devez pas me croire, vous pouvez le vérifier en supprimant nogil et en appelant Cython. Dans le créé * .c-fichier que vous pouvez voir le C-signature de votre fonction, __Pyx_memviewslice étant la partie importante:

static CYTHON_INLINE __Pyx_memviewslice __pyx_f_4file_array_test(CYTHON_UNUSED double *__pyx_v_x) 

Ce point de vue de la mémoire est un objet Python, donc il doit être enregistré dans le collecteur des ordures et par conséquent, le verrou Global Interpreter est nécessaire - c'est la raison pour laquelle vous voyez le message d'erreur «Operation not allowed without gil».

Donc, vous avez au moins trois options:

  1. Est-il vraiment si important de le faire avec "nogil"? Si non, alors il suffit de le laisser tomber. C'est la solution la plus simple, l'inconvénient: vous pourriez perdre des performances. Utilisez un véritable tableau C, c'est-à-dire int *res = (int *) malloc(2*sizeof(int)). C'est rapide, l'inconvénient: vous devez gérer la mémoire par vous-même.
  2. Utilisez C++ et std::vector<int>, l'avantage est que vous n'avez plus besoin de gérer la mémoire, mais vous devrez passer en C++.

Une amélioration de la possibilité 1. est d'acquérir gil uniquement pour la dernière ligne, où il est nécessaire (cela ne fera pas beaucoup de différence pour cet exemple mais peut pour le code réel):

cdef inline int[:] array_test(double *x) nogil: 
    cdef int output[2] 
    output[0]=1 
    output[1]=9 
    with gil: 
     return output 

OP Comme l'a proposé, une autre possibilité serait de changer la signature de la fonction à:

cdef inline void array_test(double *x, int[:] output) nogil: 

L'astuce ici: la fuction array_test ne crée plus la tranche de vue de mémoire résultante et n'a pas à l'enregistrer dans le garbage collector, ainsi "nogil" est possible.

Il y a quelques inconvénients mineurs, comme l'appel de array_test devenir plus lourd et l'appelant doit avoir gil afin de créer l'affichage de la mémoire output. Mais il a aussi l'avantage, que l'appelant peut déterminer dans quel type de structure de données le résultat devrait être stocké (tableau numpy ou peut-être quelque chose d'autre).

+0

Merci d'avoir listé les options! Une autre façon de contourner le problème est de faire de la fonction un vide, et de lui faire modifier un tableau d'entrée vide supplémentaire. – user3433489

+0

@ user3433489 How simple! N'a pas pensé à ça ... – ead