Contactez-nous

Gestion de la mémoire et performance

Découvrez comment Python gère la mémoire avec le ramasse-miettes (garbage collector). Apprenez à optimiser votre code (profiling, bonnes pratiques), à utiliser __slots__, et à utiliser Cython ou Numba pour améliorer les performances.

Le ramasse-miettes (garbage collector) de Python : comment Python gère la mémoire

Python utilise un mécanisme automatique de gestion de la mémoire appelé ramasse-miettes (garbage collector, GC). Le GC est responsable de libérer la mémoire qui n'est plus utilisée par votre programme, afin d'éviter les fuites de mémoire (memory leaks).

Le GC de Python utilise un algorithme de comptage de références (reference counting) combiné à un détecteur de cycles (cycle detector).

Le comptage de références fonctionne en maintenant un compteur pour chaque objet en mémoire. Ce compteur représente le nombre de références à l'objet (c'est-à-dire le nombre de variables, de listes, de dictionnaires, etc., qui pointent vers l'objet). Lorsque le compteur d'un objet atteint zéro, cela signifie que l'objet n'est plus accessible, et la mémoire occupée par l'objet peut être libérée.

Cependant, le comptage de références ne peut pas détecter les cycles de références (par exemple, si une liste A contient une référence à une liste B, et que la liste B contient une référence à la liste A). Dans ce cas, les compteurs de références des deux listes ne seront jamais zéro, même si elles ne sont plus accessibles. C'est là qu'intervient le détecteur de cycles. Le détecteur de cycles est un algorithme plus complexe qui s'exécute périodiquement pour détecter et collecter les cycles de références.

En général, vous n'avez pas besoin de vous soucier du GC de Python. Il fonctionne automatiquement en arrière-plan. Cependant, il est utile de comprendre comment il fonctionne, car cela peut vous aider à écrire du code plus efficace en termes de mémoire, et à éviter certains pièges.

Nous verrons des exemples de comptage de références et de cycles de références, et comment le GC de Python les gère.

Optimisation du code : profiling et bonnes pratiques

L'optimisation du code consiste à améliorer les performances de votre programme, en réduisant son temps d'exécution et/ou sa consommation de mémoire.

Avant d'optimiser votre code, il est important de l'analyser pour identifier les goulots d'étranglement (bottlenecks), c'est-à-dire les parties du code qui prennent le plus de temps à s'exécuter. C'est ce qu'on appelle le profiling.

Python fournit plusieurs outils pour le profiling, comme les modules `profile` et `cProfile`, et le module `timeit`. Ces outils vous permettent de mesurer le temps d'exécution de différentes parties de votre code, et de visualiser les résultats sous forme de rapports ou de graphiques.

Une fois que vous avez identifié les goulots d'étranglement, vous pouvez essayer d'optimiser ces parties de votre code. Il existe de nombreuses techniques d'optimisation, comme :

  • Utiliser des structures de données et des algorithmes efficaces.
  • Eviter les boucles imbriquées inutiles.
  • Utiliser des compréhensions de listes ou des expressions génératrices plutôt que des boucles `for` lorsque c'est possible.
  • Utiliser des fonctions intégrées (built-in functions) plutôt que de réimplémenter la même fonctionnalité.
  • Eviter les copies inutiles de données.
  • Utiliser la vectorisation avec NumPy pour les opérations sur des tableaux.

Il est important de noter que l'optimisation prématurée est souvent une mauvaise idée. Vous ne devez optimiser votre code que lorsque vous avez identifié un problème de performance réel, et que vous avez mesuré l'impact de l'optimisation.

Nous verrons comment utiliser les outils de profiling, comment interpréter les résultats, et comment appliquer quelques techniques d'optimisation courantes.

Utilisation de `__slots__` pour optimiser la mémoire : réduisez l'empreinte mémoire de vos objets

Par défaut, Python stocke les attributs d'un objet dans un dictionnaire (`__dict__`). Cela rend l'accès aux attributs très flexible, mais cela consomme également plus de mémoire, car un dictionnaire prend plus de place qu'une structure de données plus compacte.

Si vous avez besoin de créer un grand nombre d'objets d'une même classe, et que vous savez à l'avance quels attributs ces objets auront, vous pouvez utiliser `__slots__` pour réduire la consommation de mémoire.

`__slots__` est un attribut de classe spécial qui vous permet de spécifier une liste fixe d'attributs pour les objets de la classe. Au lieu d'utiliser un dictionnaire, Python utilisera alors une structure de données plus compacte pour stocker les attributs.

Pour utiliser `__slots__`, vous définissez une variable de classe nommée `__slots__`, et vous lui affectez une liste ou un tuple de chaînes de caractères, contenant les noms des attributs. Par exemple : `class MaClasse: __slots__ = ['attribut1', 'attribut2']`.

L'utilisation de `__slots__` peut réduire considérablement la consommation de mémoire, en particulier si vous avez un grand nombre d'objets. Cependant, elle a aussi quelques limitations :

  • Vous ne pouvez pas ajouter dynamiquement de nouveaux attributs aux objets (sauf si vous incluez `'__dict__'` dans la liste `__slots__`, mais cela annule en partie l'avantage de `__slots__`).
  • Les classes qui utilisent `__slots__` ne peuvent pas utiliser l'héritage multiple (sauf si les classes mères utilisent également `__slots__`).

Nous verrons comment utiliser `__slots__`, quels sont ses avantages et ses limitations, et quand l'utiliser.

Utilisation de Cython ou de Numba pour améliorer les performances : compilez votre code Python

Si vous avez besoin d'améliorer les performances de certaines parties critiques de votre code Python, vous pouvez utiliser des outils comme Cython ou Numba.

Cython est un langage de programmation qui est un sur-ensemble de Python. Il vous permet d'ajouter des annotations de type à votre code Python, et de le compiler en code C, qui peut s'exécuter beaucoup plus rapidement que le code Python interprété.

Numba est une bibliothèque qui vous permet de compiler des fonctions Python (en particulier des fonctions qui utilisent des tableaux NumPy) en code machine, en utilisant le compilateur LLVM. Numba utilise la compilation à la volée (just-in-time, JIT), ce qui signifie que le code est compilé au moment de l'exécution, la première fois qu'il est appelé.

Cython et Numba peuvent offrir des améliorations de performance significatives, en particulier pour les codes numériques ou les codes qui contiennent des boucles imbriquées. Cependant, ils nécessitent un peu plus de travail que l'écriture de code Python pur.

Nous verrons comment utiliser Cython et Numba, comment compiler votre code, et quels types de code peuvent bénéficier de ces outils.