2017-10-02 7 views
0

Je suis très nouveau à Cython et après avoir parcouru la documentation Cython je suis tombé sur Cython Types d'extension. Je me demande quel avantage cela donne sur les Classes Python normales. J'essayais de convertir ma classe Python précédente qui avait des listes dans ses membres de données à un type d'extension Cython mais il semble que je ne puisse pas déclarer des listes en tant que membres de données en type d'extension. La seule chose que je peux faire est de convertir une classe python qui utilise des membres de données de types de données C primitifs en type extension Cython.Comprendre les avantages de l'utilisation des types d'extension Cython vs Python Classes

J'ai un segment de mon code en classe python que j'ai besoin d'optimiser en utilisant Cython. Comment est-ce que je peux cytoniser des fonctions liées à ce segment sans déclarer la classe comme type d'extension de Cython? (En fait, je veux déclarer que certaines fonctions comme CDEF, pas tous)

Répondre

2

Je ne l'ai pas utilisé ces cdef classes ées lorsque le code écrit dans Cython, donc je vais laisser cette partie sur les avantages de cdef types ées pour quelqu'un d'autre répondre. Sur le point des listes, bien que vous puissiez toujours utiliser les listes Python comme normales, vous aurez besoin de copy each member explicitly into a C array pour profiter pleinement des accélérations de l'écriture en C. Cependant, si les données sont stockées dans un tableau NumPy , vous pouvez simplement stocker un pointeur au début du tableau (en vous assurant que le tableau est contigu). Vous trouverez le tableau complet d'équivalence des types numpy et C ici: https://github.com/cython/cython/blob/master/Cython/Includes/numpy/__init__.pxd

En ce qui concerne les méthodes de classe cythonising, puisque la plupart du code Python peut fonctionner comme cela est sans modification Cython, vous pouvez définir votre classe dans Cython en utilisant class SomeClass: normalement. Puisque les fonctions cdef ne peuvent être appelées qu'à partir de Cython, vous voudrez probablement définir des méthodes cythonisées en dehors de la classe (de préférence typée pour améliorer les performances). À l'intérieur de la classe, vous pouvez utiliser le def régulier (qui peut être appelé depuis Python) pour appeler leur contrepartie cytonisée.

Pour les classes Python majoritairement majoritairement que vous ne souhaitez pas déplacer dans Cython, vous pouvez utiliser une méthode similaire, mais vous ne disposez que de def fonctions ed dans Cython appelant la version ed cdef. Vous appelez ensuite les fonctions Cython de Python comme vous le feriez normalement lors de l'importation de modules.

Pour les structures de données qui ont uniquement besoin de résider en C et qui n'interagissent pas (beaucoup) avec Python, vous pouvez également envisager d'utiliser PyCapsule pour les stocker en tant qu'attributs de classe.

Edit:

À la lecture du commentaire sous chrisb réponse de, je suppose que vous voulez avoir un tableau 2D avec une longueur de ligne variable. Avant de plonger directement dans l'implémentation de la même structure de données en C, il est important de noter que C n'est pas Python. Il ne gérera pas automatiquement la longueur des listes pour vous, mais vous devrez gérer vous-même la mémoire (voir l'exemple dans le premier lien). Bien que ce soit la manière standard d'allouer dynamiquement de la mémoire, les programmeurs qui ne sont pas habitués à C (et Cython) ont tendance à ne pas toucher "malloc et ses amis" puisque les pointeurs voleront autour. En plus de cela, les tableaux n'ont généralement pas de type de données mélangé, par ex. vous ne pouvez pas avoir à la fois des nombres et des chaînes dans le même tableau (si vous avez vraiment besoin de , il y a un moyen de le faire). À la lumière de cela, vous voudrez peut-être repenser votre structure de données. Par exemple, vous pouvez envisager de représenter les données avec des tableaux de longueur constante.Si votre tableau a une largeur maximale, vous pouvez échanger de la mémoire avec une facilité de programmation en utilisant un tableau NumPy.

Si vous êtes heureux de donner la gestion de la mémoire manuelle d'un coup, voici un exemple simple d'allocation de mémoire pour un tableau 2D de int s:

from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free 

cdef int **generate_2D_array(int rows, int columns): 
    cdef int row 
    cdef int **parent = <int **>PyMem_Malloc(rows * sizeof(int*)) 
    if not parent: 
     raise MemoryError() 
    for row in range(rows): 
     parent[row] = <int *>PyMem_Malloc(columns * sizeof(int)) 
     if not parent[row]: 
      raise MemoryError() 
    return parent 

Pour modifier la longueur d'une ligne, vous pouvez utiliser :

cdef void resize_row(int *row_pointer, int new_size): 
    PyMem_Realloc(row_pointer, new_size) 

Lorsque vous avez terminé avec les données, souvenez-vous de désaffecter la mémoire en utilisant PyMem_Free de façon similaire à l'allocation avec PyMem_Malloc. La règle générale est: pour chaque PyMem_Malloc vous utilisez, libérer la mémoire en utilisant exactement un PyMem_Free, ni plus, ni moins. Et enfin, juste un mot d'avertissement, le fait de ne pas les utiliser de manière appropriée peut provoquer des erreurs de segmentation, des fuites de mémoire ou un comportement indéfini.

0

Pour les opérations lourdes python, une classe cdef conservera des avantages en termes de performances, en raison de l'optimisation de l'accès aux champs et de la suppression de l'en-tête python. Exemple simple:

class PyThing: 
    def __init__(self, data): 
     self.data = data 
    def calc(self): 
     ans = 0 
     for v in self.data: 
      ans += v 
     return ans 


py = PyThing(list(range(100000))) 

%timeit py.calc() 
4.07 ms ± 29.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 


import cython 
%load_ext cython 

%%cython 
cdef class CyThing: 
    cdef list data 
    def __init__(self, data): 
     self.data = data 
    def calc(self): 
     ans = 0 
     for v in self.data: 
      ans += v 
     return ans 

cy = CyThing(list(range(100000))) 

%timeit cy.calc() 
2.8 ms ± 123 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 

Ceci est au prix de la simplicité/debug-capacité, donc peut ou peut ne pas valoir la peine. Une meilleure utilisation est lorsque vous avez des valeurs de niveau c que vous voulez stocker et utiliser, les avantages de performance peuvent être beaucoup plus importants.

+0

Salut! Merci d'avoir répondu! Cependant je ne savais pas que nous pouvions aussi utiliser des listes en cython! Est-ce mieux dans les performances par rapport à la liste Python? Est-ce que le gain de performance passe à cause de ça? Puis-je aussi déclarer de telles listes 2D en cython? –

+1

Oui, 'list' peut être utilisé dans Cython, c'est exactement le même objet qu'une liste python, mais Cython sera un peu plus rapide en générant du code plus spécialisé et en ignorant la surcharge de l'interpréteur python. – chrisb

+0

Si vous connaissez le type/la forme de vos données, vous feriez mieux d'utiliser un tableau numpy, qui peut être déclaré comme bidimensionnel comme suggéré par @Capow, une liste python ne peut pas (sera simplement une liste imbriquée de listes) – chrisb