Comme il y a plus d’une façon de faire en Python, trouver l’approche la plus efficace en termes de mémoire pour certaines tâches peut s’avérer difficile. C’est là qu’un profileur de mémoire peut être utile. En plus de traquer les fuites, l’estimation du profil mémoire de votre code permet de déterminer quel code est efficace en termes de mémoire.



Que vous développiez un modèle d’apprentissage automatique ou un site web avec Python, vous pouvez estimer le profil de mémoire des scripts, des lignes de code individuelles ou des fonctions.


Quelques outils pour estimer le profil de mémoire en Python

Estimer le profil mémoire de l’ensemble de votre base de code peut s’avérer peu pratique, car cela peut ralentir votre application de manière significative. Il est préférable de profiler sélectivement les fonctions ou les méthodes que vous soupçonnez de consommer plus de mémoire. Mais même si vous voulez faire cela pour l’ensemble de votre application, vous pourriez vouloir dédier un module isolé à cette tâche.

Il existe de nombreuses bibliothèques de profilage en Python. Les plus populaires sont memory_profiler, psutil, Tracemallocet pympler. Ce tutoriel utilise memory_profiler et psutil.

Tandis que psutil est idéal pour estimer la consommation totale de mémoire lors de l’exécution d’une méthode ou d’une fonction, memory_profiler fournit des informations plus détaillées sur l’utilisation de la mémoire, y compris les tendances d’utilisation ligne par ligne et au niveau fonctionnel.

Pour commencer, installez memory_profiler dans votre environnement virtuel Python. Ceci installe également psutil.

 pip install memory_profiler

Obtenir la taille d’un objet en mémoire

Vous pouvez commencer votre profilage de la mémoire en calculant d’abord la taille d’un objet que vous avez l’intention d’utiliser en mémoire.

Ce type de profilage est utile au début du développement, lorsqu’il s’agit de déterminer le type d’objet à utiliser dans un programme.

Par exemple, si vous n’arrivez pas à décider quelles méthodes utiliser pour accomplir une tâche, par exemple le type de données Python approprié, vous pouvez obtenir la taille de chacune en octets afin de déterminer laquelle est la plus légère pour votre cas d’utilisation.

Le sys.getsizeof La méthode intégrée est très utile ici :

 import sys
print(f"list size: {sys.getsizeof([])} bytes")
print(f"dictionary size: {sys.getsizeof(dict)} bytes")
print(f"tuple size: {sys.getsizeof(())} bytes")
print(f"set size: {sys.getsizeof({})} bytes")

Voici le résultat :

Profil de mémoire pour les types de données

Vous pouvez également utiliser la fonction sys.getsizeof pour comparer la taille de la mémoire d’une fonction intégrée et d’une fonction personnalisée.

Par exemple, comparez cette fonction de longueur personnalisée qui utilise une boucle for Python avec la fonction intégrée len fonction :

 import sys

def getLength(iterable):
    count = 0

    for i in iterable:
        count +=1

    return count

print(f"Built-in length function: {sys.getsizeof(len)} bytes")
print(f"Custom length function: {sys.getsizeof(getLength)} bytes")

Le code ci-dessus donne le résultat suivant :

sortie de la ligne de commande comparaison de la mémoire

Cependant, alors que les sys.getsizeof mesure la taille d’un objet en mémoire, mais elle ne tient compte que de l’objet lui-même et non de ceux qui y font référence. Pour cela, vous aurez besoin d’une méthode de profilage plus détaillée.


Trouver le profil mémoire d’une fonction Python

Vous pouvez obtenir un profil de mémoire plus détaillé de chaque ligne de code d’une fonction en utilisant la fonction memory_profiler . Il s’agit d’ajouter l’élément @profil à votre fonction ou méthode :

 import pandas
import numpy
from memory_profiler import profile

class Manipulate:
    @profile
    def manipulateData(self):
        df = pandas.DataFrame({
            'A' :[0, 3, numpy.nan, 10, 3, numpy.nan],
            'B' : [numpy.nan, "Pandas", numpy.nan, "Pandas", "Python", "JavaScript"],
        })

        df.fillna(method='bfill', inplace=True)
        df.fillna(method='ffill', inplace=True)
        return str(df)

manip = Manipulate()
print(manip.manipulateData())

Le code ci-dessus donne un profil détaillé de la mémoire de chaque ligne de code dans la fonction, comme indiqué :

Sortie de code pour le profilage de la mémoire ligne par ligne

Le Utilisation de la mémoire indique l’utilisation de la mémoire pour une ligne de code particulière, tandis que la colonne Incrément montre la contribution de chaque ligne aux frais généraux. La colonne Occurrence La colonne Occurrence définit le nombre de fois qu’une ligne de code alloue ou désalloue de la mémoire.

Par exemple, dans la sortie ci-dessus, la ligne 11 est apparue deux fois avec un incrément de mémoire de 0,1 MiB (Mebibyte), augmentant l’utilisation de la mémoire à 55,4 MiB. Les lignes 19 et 22 ont également contribué à hauteur de 0,2 Mio et 0,3 Mio, respectivement, ce qui porte l’utilisation totale de la mémoire à 55,9 Mio.


Trouver le profil mémoire d’un script Python par horodatage

Vous pouvez également estimer le profil de mémoire d’un script Python entier à l’aide de la fonction memory_profiler en exécutant la commande mprof dans le terminal comme indiqué :

 mprof run script_name.py

La commande ci-dessus échantillonne le script spécifié toutes les 0,1s et crée automatiquement un fichier .dat dans le répertoire de votre projet actuel.

Profil mémoire d'un script Python

Les figures qui suivent le MEM sont les profils d’utilisation de la mémoire du script Python à un intervalle de temps spécifique. Les derniers chiffres à droite représentent l’horodatage capturé par le profileur pour chaque utilisation de la mémoire.

Vous pouvez également obtenir un graphique du profil de mémoire. Cela nécessite l’installation de matplotlib:

 pip install matplotlib

Une fois installé, exécutez le programme mprof comme suit :

 mprof plot

Voici le résultat dans ce cas :

Ligne de commande tracé sortie-1

Exécuter le profil de mémoire du script dans un fichier Python dédié

Vous pouvez souhaiter établir un profil pour différents scripts Python. Vous pouvez le faire en utilisant un module Python dédié via la fonction sous-processus.

De cette façon, vous pouvez séparer votre profileur de mémoire de votre base de code et sauvegarder la sortie du graphique localement :

 import subprocess

subprocess.run([
    'mprof', 'run', '--include-children', 'missing.py'
])

# save the plot output locally
subprocess.run(['mprof', 'plot', '--output=output.jpg'])

Pour exécuter le profil de mémoire du script, il vous suffit d’exécuter le fichier Python contenant le code ci-dessus. Cela génère un graphique de profil de mémoire (sortie.jpg) dans le répertoire de fichiers :

Tracé du profil de mémoire du script


Trouver la quantité de mémoire utilisée lors de l’exécution d’une fonction

Vous pouvez connaître le profil de mémoire total d’une méthode ou d’une fonction pendant son exécution à l’aide de la fonction psutil paquet.

Par exemple, pour profiler la méthode de manipulation Pandas DataFrame précédente dans un autre fichier Python :

 import psutil
import sys
import os
sys.path.append(sys.path[0] + "/..")

# import the class containing your method
from somecode.missing import Manipulate

# instantiate the class
manip = Manipulate()

process = psutil.Process(os.getpid())
initial_memory = process.memory_info().rss

# run the target method:
manip.manipulateData()

# get the memory information after execution
final_memory = process.memory_info().rss
memory_consumed = final_memory - initial_memory
memory_consumed_mb = memory_consumed / (1024 * 1024)
print(f"Memory consumed by the function: {memory_consumed_mb:.2f} MB")

Le tableau ci-dessus estime le profil mémoire total de la méthode en mégaoctets (Mo), comme indiqué :

Profil de mémoire en sortie de la ligne de commande totale


Trouver le profil de mémoire d’une ligne de code dans Jupyter Notebook

Si vous utilisez iPython dans Jupyter Notebook, vous pouvez calculer le profil de mémoire d’une ligne de code à l’aide de la fonction memory_profiler. Il suffit de charger memory_profiler dans une cellule. Ajoutez ensuite le %memit fonction magique à votre code dans les cellules suivantes ; elle renvoie la mémoire maximale du code et sa taille incrémentée.

Cette méthode ne fonctionne pas avec les scripts Python ordinaires en dehors de iPython dans Jupyter Notebook.

Par exemple :

Code et sortie du notebook Jupyter

Vous pouvez également utiliser l’option %memit Fonction magique du Bloc-notes de Jypyter permettant de profiler la mémoire d’une fonction au moment de l’exécution :

Profil de la mémoire d'une fonction Jupyter Notebook


Améliorez l’efficacité de votre mémoire dans votre code Python

Compte tenu des tâches lourdes de collecte de données pour lesquelles nous utilisons souvent Python, chaque ligne de code a besoin d’une optimisation adéquate pour gérer l’utilisation de la mémoire. Bien que Python propose de nombreuses fonctions intégrées, les objets non référencés entraînent des fuites de mémoire.

Si vous avez intégré toutes les syntaxes Python qui fonctionnent dans votre base de code sans tenir compte de l’utilisation de la mémoire, vous devriez peut-être revenir en arrière avant d’aller trop loin.