Selon l’enquête Stackoverflow de 2019, le langage de programmation Python a recueilli 73,1% d’approbation parmi les développeurs. Il se classe au deuxième rang derrière Rust et continue de dominer en science des données et en apprentissage automatique (ML).
Python est le favori des développeurs. C’est un langage de haut niveau connu pour sa robustesse et sa philosophie de base – la simplicité plutôt que la complexité. Cependant, les performances de l’application Python sont une autre histoire. Comme toute autre application, elle a son lot de problèmes de performances.
La plupart du temps, les outils APM tels que Retrace peuvent aider à résoudre les problèmes de performances des applications. Mais que se passe-t-il si votre application Python fonctionne depuis quatre heures et que le serveur est à court de mémoire? C’est un problème spécifique impliquant des ressources de mémoire.
C’est ce qu’on appelle une fuite de mémoire. Les développeurs doivent trouver le coupable. C’est à ce moment que les profileurs de mémoire Python entrent en jeu.
Explorons plus loin.
Qu’est-ce que les profileurs de mémoire Python ?
Les applications de profilage impliquent toujours des problèmes tels que le processeur, la mémoire, etc. Cependant, les applications Python sont sujettes à des problèmes de gestion de la mémoire. Ceci est principalement dû au fait que Python est appliqué à la science des données et aux applications de ML et fonctionne avec de grandes quantités de données. De plus, Python s’appuie sur son système de gestion de la mémoire par défaut, au lieu de le laisser à l’utilisateur.
Comme le code Python fonctionne dans les conteneurs via un framework de traitement distribué, chaque conteneur contient une quantité fixe de mémoire. Si l’exécution du code dépasse la limite de mémoire, le conteneur se terminera. C’est à ce moment que le développement subit des erreurs de mémoire.
Cependant, ce n’est pas toujours le cas. Il y a des cas où les développeurs ne savent pas ce qui se passe. Peut-être qu’un objet est suspendu à une référence alors qu’il n’est pas censé l’être et s’accumule avec le temps. Une fois qu’il atteint son apogée, des problèmes de mémoire surviennent.
La solution rapide consiste à augmenter l’allocation de mémoire. Cependant, ce n’est pas pratique car cela peut entraîner un gaspillage de ressources. En outre, cela peut compromettre la stabilité de l’application en raison de pics de mémoire imprévisibles.
Par conséquent, nous avons besoin de l’aide de profileurs de mémoire Python. Le but des profileurs de mémoire Python est de détecter les fuites de mémoire et d’optimiser l’utilisation de la mémoire dans vos applications Python. Ces types de profileurs de mémoire Python comprennent l’efficacité de l’espace du code et des paquets utilisés.
Les meilleurs profileurs de mémoire Python
Bien que Python gère automatiquement la mémoire, il a besoin d’outils car les travaux Python de longue durée consomment beaucoup de mémoire. Dans la plupart des cas, ces travaux ne renverront pas la mémoire au système d’exploitation jusqu’à la fin du processus, même s’il exécute correctement la collecte des ordures.
Voici une liste de profileurs de mémoire Python connus:
Pympler
Jean Brouwers, Ludwig Haehne et Robert Schuppenies ont construit Pympler en août 2008. Ils ont introduit le processus de pympling, dans lequel Pympler obtient des détails sur la taille et la durée de vie des objets Python.
Le profileur de mémoire Python de Pympler analyse le comportement de la mémoire de l’objet Python dans une application en cours d’exécution. Il fournit une solution de profilage de mémoire Python complète et autonome. En outre, il projette une erreur possible dans le comportement d’exécution comme le gonflement de la mémoire et d’autres « pymples ». »
Il y a trois modules distincts à l’intérieur de Pympler.
- Le module asizeof fournit les informations de taille de l’objet Python.
- Le module muppy permet la surveillance en ligne d’une application Python.
- Le module de suivi de classe fournit une analyse hors ligne de la durée de vie des objets Python sélectionnés.
Tout d’abord, utilisons asizeof pour étudier la quantité de mémoire consommée par certains objets Python.
>>> from pympler import asizeof
>>> obj =
>>> asizeof.asizeof(obj)
>>> print (asizeof.asized(obj, detail=1).format())
size=192 flat=48
(6, 4) size=64 flat=32
‘i’ size=32 flat=32
3 size=16 flat=16
2 size=16 flat=16
1 size=16 flat=16
Deuxièmement, implémentons le module muppy:
>>>de pympler import muppy
>>>>allObjects=muppy.get_objects()
>>>len(allObjects)
>>>>du résumé de l’importation de pympler
>>>>I= résumé.resume(allObjects)
>>>résumé.print_(sum)
types | | # objects | | total size |
========================== | | =========== | | ============ |
str | | 13262 | | 1.01 MB |
dict | | 2120 | | 659.99 KB |
code | | 3362 | | 343.73 KB |
list | | 2587 | | 247.46 KB |
type | | 639 | | 242.61 KB |
tuple | | 2069 | | 58.83 KB |
set | | 86 | | 44.57 KB |
wrapper_descriptor | | 1247 | | 43.84 KB |
builtin_function_or_method | | 1014 | | 35.65 KB |
method_descriptor | | 937 | | 32.94 KB |
abc.ABCMeta | | 66 | | 32.38 KB |
weakref | | 818 | | 28.76 KB |
int | | 1612 | | 24.72 KO |
getset_descriptor| | 555 | | 17,34 Ko |
frozenset| | 90| | 16,87 Ko |
Ici, vous pouvez afficher tous les objets Python dans un tas en utilisant le module muppy. Vous pouvez appeler un autre résumé et le comparer pour vérifier si certains tableaux présentent des fuites de mémoire. En savoir plus sur le module muppy ici.
Le troisième module du profileur Pympler est le Class Tracker. Il suit la durée de vie des objets de certaines classes. Thus, it provides insight into instantiation patterns and helps developers understand how specific objects contribute to the memory footprint in the long run.
>>> tr = classtracker.ClassTracker()
>>> tr.track_class(Document)
>>> tr.create_snapshot(description=’Snapshot 1′)
>>> doc = create_document()
>>> tr.create_snapshot(description=’Snapshot 2′)
>>> tr.stats.print_summary()
SUMMARY RÉSUMÉ ———————-
Instantané 1 actif 0 B pct moyen
Instantané 2 actif 0 B pct moyen
—————————
Pour en savoir plus sur Class Tracker, cliquez ici.
Guppy3
Guppy3 (également connu sous le nom de Heapy) est un environnement de programmation Python et un ensemble d’outils d’analyse de tas. C’est un paquet qui contient les sous-paquets suivants :
- etc – C’est un module de support qui a le module de protocole Glue.
- gsl – Le sous-paquet qui contient l’implémentation du langage de spécification Guppy. Il crée des documents et des tests à partir d’une source commune.
- heapy – Le jeu d’outils d’analyse de tas fournit des informations d’objet sur le tas et affiche les informations.
- ensembles – Cela contient des ensembles de bits et des ensembles de nœuds.
Guppy3 est un fork de Guppy-PE et a été construit par Sverker Nilsson pour Python 2.
Remarque : L’utilisation de ce profileur de mémoire Python nécessite Python 3.5, 3.6, 3.7 ou 3.8. Ce paquet fonctionne uniquement pour CPython. Par conséquent, PyPy et les autres implémentations du compilateur Python ne sont pas prises en charge. De plus, pour utiliser le navigateur graphique, il a besoin de Tkinter. De plus, le filetage doit être disponible lors de l’utilisation d’un moniteur distant.
Voici comment tirer parti de ce profileur de mémoire Python. Vous pouvez prendre un instantané du tas avant et après un processus critique. Ensuite, comparez la mémoire totale et identifiez les pics de mémoire possibles impliqués dans les objets communs.
>>> from guppy import hpy
>>> h=hpy()
>>> h.heap()
Partition of a set of 34090 objects. Total size = 2366226 bytes.
Index | Count | % | Size | % | Cumulative | % | Kind (class / dict of class) |
0 | 10279 | 30 | 666873 | 28 | 666873 | 28 | str |
1 | 4697 | 14 | 259576 | 11 | 926449 | 39 | bytes |
2 | 2413 | 7 | 251684 | 11 | 1178133 | 50 | types.CodeType |
3 | 6825 | 20 | 238084 | 10 | 1416217 | 60 | tuple |
4 | 448 | 1 | 174868 | 7 | 1591085 | 67 | type |
5 | 2208 | 6 | 150144 | 6 | 1741229 | 74 | function |
6 | 448 | 1 | 130964 | 6 | 1872193 | 79 | dict of type |
7 | 94 | 0 | 83532 | 4 | 1955725 | 83 | dict of module |
8 | 242 | 1 | 56524 | 2 | 2012249 | 85 | dict (no owner) |
9 | 1133 | 3 | 40788 | 2 | 2053037 | 87 | types.WrapperDescriptorType |
<118 more rows. Type e.g. ‘_.more’ to view.>
Memory Profiler
Memory Profiler is a pure Python module that uses the psutil module. Il surveille la consommation de mémoire d’un processus de travail Python. En outre, il effectue une analyse ligne par ligne de la consommation de mémoire de l’application.
Le mode d’utilisation de la mémoire ligne par ligne fonctionne de la même manière que le line_profiler.
- Il décore la fonction que vous souhaitez profiler à l’aide de la fonction @profile.
- Vous pouvez exécuter le script avec un script spécial. Par exemple, utilisez des arguments spécifiques à l’interpréteur Python.
Dans l’exemple suivant, avons une fonction simple appelée my_func. Cette fonction crée une liste avec une plage spécifiée.
@profile
def my_func():
a=
for i in range(1000):
a.append(i)
my_func()
This outputs:
Line # | Mem Usage | Increment | Line Contents |
1 | 13.859 MiB | 13.859 MiB | @profile |
2 | def my_func(): | ||
3 | 13.859 MiB | 0.000 MiB | a= |
4 | 13.859 MiB | 0.000 MiB | pour i dans la plage (1000): |
5 | 13,859 MiB | 0,000 MiB | a.append(i) |
La première colonne est le numéro de ligne du code profilé. L’utilisation de Mem est l’utilisation de la mémoire de l’interpréteur Python après chaque exécution de code. La troisième colonne (Incrément) représente la différence de mémoire entre la ligne courante et la dernière. La dernière colonne (Contenu de la ligne) affiche les codes profilés.
To see how this Python memory profiler works, let’s change the range value to 1000000 in the function above and execute it. Here is the output:
Line # | Mem usage | Increment | Line Contents |
1 | 13.844 MiB | 13.844 MiB | @profile |
2 | def my_func(): | ||
3 | 13.844 MiB | 0.000 MiB | a= |
4 | 33.387 MiB | 0.016 MiB | pour i dans la plage (1000000): |
5 | 33,387 MiB | 0,293 MiB | a.append(i) |
Les lignes 4 et 5 montrent une augmentation de l’utilisation de la mémoire, prouvant que ce profileur effectue une analyse ligne par ligne de la consommation de mémoire.
Fil
Fil profiler est un profileur de mémoire Python open source. Il convient aux applications de traitement de données et de calcul scientifique. Actuellement, il est encore au stade de développement et fonctionne uniquement sous Linux et macOS.
La plupart des Data Scientists et des développeurs Python sont confrontés à des problèmes de mémoire avec le pipeline de données Python. Lorsqu’il utilise trop de mémoire, il est difficile de déterminer où va exactement toute la mémoire.
Par exemple, citons deux scénarios :
Serveurs
Comme les serveurs fonctionnent sans arrêt, les fuites de mémoire sont souvent la cause d’une défaillance des performances. Les développeurs négligent de petites quantités de fuites de mémoire car la plupart des serveurs traitent de petites quantités de données à la fois. Cependant, ceux-ci peuvent totaliser des dizaines de milliers d’appels. En conséquence, cela pourrait créer de graves problèmes de production au fil du temps.
Pipelines de données
Lors du traitement de gros morceaux de données, les pics d’utilisation de la mémoire constituent d’énormes menaces pour les pipelines de données. Par exemple, si votre application utilise 1 Go de RAM pendant un certain temps et a soudainement besoin de 16 Go de RAM. Il y a un grand besoin d’identifier les causes des pics de mémoire soudains.
C’est l’objectif principal de Fil : diagnostiquer les pics d’utilisation de la mémoire, quelle que soit la quantité de données traitées. Il indique exactement où se trouve le pic d’utilisation de la mémoire et quel code est responsable de ce pic.
Bien qu’il existe des profileurs de mémoire Python existants qui mesurent l’utilisation de la mémoire, il a des limites. L’un d’eux traite de grandes quantités de traitement par lots de données. Les applications Python sont principalement des applications de traitement par lots dans lesquelles elles lisent constamment des données, les traitent et affichent le résultat.
Ce problème est résolu par notre prochain profileur.
Blackfire
Pour un langage très dynamique comme Python, la plupart des développeurs rencontrent des problèmes de mémoire pendant le déploiement. Cela conduit à une certaine confusion quant à ce qui arrive à l’utilisation de la mémoire. Les développeurs ont tendance à effectuer des optimisations mais n’ont pas les bons outils à utiliser.
Blackfire est un profileur de mémoire Python propriétaire (peut-être le premier. Il utilise le gestionnaire de mémoire de Python pour tracer chaque bloc de mémoire alloué par Python, y compris les extensions C. Blackfire est nouveau sur le terrain et vise à résoudre les problèmes de fuites de mémoire tels que:
- objets volumineux en mémoire qui ne sont pas libérés
- cycles de référence
- comptage de références invalide dans les extensions C provoquant des fuites de mémoire
- pics de mémoire soudains
Avec ces cas d’utilisation, Blackfire assure aux utilisateurs qu’il a une surcharge très limitée et n’a pas d’impact sur les utilisateurs finaux car il mesure la consommation de mémoire de l’application Python au niveau de l’appel de fonction.
Le profileur de mémoire Python Blackfire utilise l’API PyMem_SetAllocator pour tracer les allocations de mémoire comme tracemalloc. À l’heure actuelle, Blackfire prend en charge les versions Python 3.5 et supérieures. Vous pouvez visiter son site pour en savoir plus.
Profilage avec Retrace
Si vous travaillez avec Python, vous constatez en quelque sorte qu’il ne libère pas immédiatement de la mémoire dans le système d’exploitation. Par conséquent, vous l’exécutez dans un processus séparé pour vous assurer que la mémoire est libérée après l’exécution d’un morceau de code. Cela se fait grâce à une approche utile appelée « petit cas de test. »Ce processus permet d’exécuter uniquement le code de fuite de mémoire en question.
Lorsque vous traitez de grandes quantités de données, utilisez un sous-ensemble des données échantillonnées aléatoirement. Exécutez également des tâches gourmandes en mémoire dans des processus distincts et utilisez des débogueurs pour ajouter des références aux objets. Cependant, considérez que l’utilisation d’un débogueur de point d’arrêt tel que pdb permet à tous les objets créés et référencés manuellement à partir du débogueur de rester dans le profil de mémoire. Cela entraînera une fausse impression de fuites de mémoire car les objets ne sont pas libérés à temps. De plus, envisagez de rechercher des paquets qui peuvent fuir. Il existe des bibliothèques Python qui pourraient potentiellement avoir des fuites de mémoire.
Maintenant, vous savez déjà comment fonctionnent les profileurs de mémoire Python et les problèmes de mémoire courants avec Python. Mais des outils tels que Retrace avec journalisation centralisée, suivi des erreurs et profilage de code peuvent vous aider à diagnostiquer les problèmes Python à plus grande échelle. Retrace de Stackify vous aidera à faire face à tous les types de pièges de performance et à maintenir le bon fonctionnement de votre code.
Commencez votre essai Retrace GRATUIT de 14 jours dès aujourd’hui !