Maison > développement back-end > Tutoriel Python > Maîtrise de la mémoire Python : améliorez les performances et éliminez les fuites de mémoire

Maîtrise de la mémoire Python : améliorez les performances et éliminez les fuites de mémoire

Barbara Streisand
Libérer: 2024-11-19 17:06:03
original
685 Les gens l'ont consulté

Python Memory Mastery: Boost Performance and Crush Memory Leaks

La gestion de la mémoire en Python est un sujet fascinant qui passe souvent inaperçu auprès de nombreux développeurs. Mais comprendre comment cela fonctionne peut sérieusement améliorer votre jeu de codage. Examinons de plus près certains concepts avancés, en particulier les faibles références et le garbage collection cyclique.

Tout d’abord, parlons des références faibles. Ce sont des outils plutôt sympas qui vous permettent de faire référence à un objet sans augmenter son nombre de références. Cela peut être très utile lorsque vous essayez d'éviter les fuites de mémoire ou les références circulaires.

Voici un exemple simple d'utilisation des références faibles :

import weakref

class MyClass:
    def __init__(self, name):
        self.name = name

obj = MyClass("example")
weak_ref = weakref.ref(obj)

print(weak_ref())  # Output: <__main__.MyClass object at ...>
del obj
print(weak_ref())  # Output: None
Copier après la connexion
Copier après la connexion
Copier après la connexion

Dans cet exemple, nous créons une référence faible à notre objet. Lorsque nous supprimons l'objet d'origine, la référence faible devient automatiquement None. Cela peut être très utile dans les scénarios de mise en cache ou lors de la mise en œuvre de modèles d'observateur.

Maintenant, plongeons-nous dans le garbage collection cyclique. Python utilise le comptage de références comme méthode principale de récupération de place, mais il dispose également d'un garbage collector cyclique pour gérer les cycles de référence. Ces cycles peuvent se produire lorsque des objets se référencent les uns les autres, créant une boucle qui empêche le nombre de références d'atteindre zéro.

Le garbage collector cyclique fonctionne en vérifiant périodiquement ces cycles et en les interrompant. Vous pouvez réellement contrôler quand cela se produit en utilisant le module gc :

import gc

# Disable automatic garbage collection
gc.disable()

# Do some memory-intensive work here

# Manually run garbage collection
gc.collect()
Copier après la connexion
Copier après la connexion

Ce niveau de contrôle peut être très utile dans les sections critiques en termes de performances de votre code. Vous pouvez retarder la collecte des ordures jusqu'à un moment plus opportun, ce qui pourrait accélérer votre programme.

Mais qu’en est-il de la détection des fuites de mémoire ? Cela peut être délicat, mais Python fournit quelques outils pour vous aider. Le module tracemalloc, introduit dans Python 3.4, est particulièrement utile :

import tracemalloc

tracemalloc.start()

# Your code here

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

print("[ Top 10 ]")
for stat in top_stats[:10]:
    print(stat)
Copier après la connexion
Copier après la connexion

Ce code vous montrera les 10 premières lignes de code qui allouent le plus de mémoire. C'est un excellent point de départ pour identifier les problèmes de mémoire potentiels.

Lorsqu'il s'agit d'optimiser l'utilisation de la mémoire dans les applications à grande échelle, vous pouvez utiliser plusieurs stratégies. L’un des plus efficaces est le pooling d’objets. Au lieu de créer et de détruire fréquemment des objets, vous pouvez maintenir un pool d'objets réutilisables :

class ObjectPool:
    def __init__(self, create_func):
        self.create_func = create_func
        self.pool = []

    def get(self):
        if self.pool:
            return self.pool.pop()
        return self.create_func()

    def release(self, obj):
        self.pool.append(obj)

# Usage
def create_expensive_object():
    # Imagine this is a resource-intensive operation
    return [0] * 1000000

pool = ObjectPool(create_expensive_object)

obj = pool.get()
# Use obj...
pool.release(obj)
Copier après la connexion
Copier après la connexion

Cette technique peut réduire considérablement les frais généraux de création et de destruction d'objets, en particulier pour les objets gourmands en ressources.

Un autre aspect important de la gestion de la mémoire consiste à comprendre comment différentes structures de données utilisent la mémoire. Par exemple, les listes en Python sont des tableaux dynamiques qui sur-allouent pour amortir le coût du redimensionnement. Cela signifie qu'ils utilisent souvent plus de mémoire que prévu :

import weakref

class MyClass:
    def __init__(self, name):
        self.name = name

obj = MyClass("example")
weak_ref = weakref.ref(obj)

print(weak_ref())  # Output: <__main__.MyClass object at ...>
del obj
print(weak_ref())  # Output: None
Copier après la connexion
Copier après la connexion
Copier après la connexion

Comme vous pouvez le constater, l'utilisation de la mémoire par la liste augmente par tranches, et non linéairement avec le nombre d'éléments. Si l'utilisation de la mémoire est critique, vous pouvez envisager d'utiliser un tuple (qui est immuable et ne peut donc pas surallouer) ou un tableau du module de tableau (qui utilise une quantité fixe de mémoire en fonction du nombre d'éléments).

Lorsque vous traitez de grands ensembles de données, vous risquez de manquer de mémoire. Dans ces cas, vous pouvez utiliser des générateurs pour traiter les données par morceaux :

import gc

# Disable automatic garbage collection
gc.disable()

# Do some memory-intensive work here

# Manually run garbage collection
gc.collect()
Copier après la connexion
Copier après la connexion

Cette approche vous permet de travailler avec des fichiers plus volumineux que votre RAM disponible.

Parlons maintenant de quelques techniques d'optimisation de la mémoire moins connues. Saviez-vous que vous pouvez utiliser des emplacements pour réduire l'empreinte mémoire de vos cours ? Lorsque vous définissez des slots, Python utilise une méthode de stockage plus efficace en mémoire pour les instances de la classe :

import tracemalloc

tracemalloc.start()

# Your code here

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

print("[ Top 10 ]")
for stat in top_stats[:10]:
    print(stat)
Copier après la connexion
Copier après la connexion

La classe slottée utilise beaucoup moins de mémoire par instance. Cela peut représenter des économies substantielles dans les programmes qui créent de nombreuses instances d'une classe.

Une autre technique intéressante consiste à utiliser des métaclasses pour implémenter un modèle singleton, ce qui peut aider à contrôler l'utilisation de la mémoire en garantissant qu'une seule instance d'une classe existe :

class ObjectPool:
    def __init__(self, create_func):
        self.create_func = create_func
        self.pool = []

    def get(self):
        if self.pool:
            return self.pool.pop()
        return self.create_func()

    def release(self, obj):
        self.pool.append(obj)

# Usage
def create_expensive_object():
    # Imagine this is a resource-intensive operation
    return [0] * 1000000

pool = ObjectPool(create_expensive_object)

obj = pool.get()
# Use obj...
pool.release(obj)
Copier après la connexion
Copier après la connexion

Cela garantit que peu importe le nombre de fois que vous essayez de créer une instance de MyClass, vous obtiendrez toujours le même objet, ce qui permettra d'économiser potentiellement de la mémoire.

En matière de mise en cache, le décorateur functools.lru_cache est un outil puissant. Il peut considérablement accélérer votre code en mettant en cache les résultats d'appels de fonctions coûteux :

import sys

l = []
print(sys.getsizeof(l))  # Output: 56

l.append(1)
print(sys.getsizeof(l))  # Output: 88

l.extend(range(2, 5))
print(sys.getsizeof(l))  # Output: 120
Copier après la connexion

Le décorateur lru_cache implémente un cache LRU (Least Récemment Utilisé), qui peut constituer une excellente stratégie de mise en cache économe en mémoire pour de nombreuses applications.

Plongeons dans quelques techniques de profilage de mémoire plus avancées. Bien que tracemalloc soit génial, vous avez parfois besoin d'informations plus détaillées. Le package memory_profiler peut fournir une analyse ligne par ligne de l'utilisation de la mémoire de votre code :

def process_large_file(filename):
    with open(filename, 'r') as f:
        for line in f:
            # Process line
            yield line

for processed_line in process_large_file('huge_file.txt'):
    # Do something with processed_line
Copier après la connexion

Exécutez ceci avec mprof run script.py puis mprof plot pour voir un graphique de l'utilisation de la mémoire au fil du temps. Cela peut être inestimable pour identifier les fuites de mémoire et comprendre le comportement de la mémoire de votre programme.

En parlant de fuites de mémoire, elles peuvent être particulièrement délicates dans les applications qui s'exécutent depuis longtemps comme les serveurs Web. Une cause fréquente est l’oubli de fermer correctement les ressources. Le module contextlib fournit des outils pour vous aider :

class RegularClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class SlottedClass:
    __slots__ = ['x', 'y']
    def __init__(self, x, y):
        self.x = x
        self.y = y

regular = RegularClass(1, 2)
slotted = SlottedClass(1, 2)

print(sys.getsizeof(regular))  # Output: 48
print(sys.getsizeof(slotted))  # Output: 16
Copier après la connexion

Ce modèle garantit que les ressources sont toujours correctement libérées, même si une exception se produit.

Lorsque vous travaillez avec de très grands ensembles de données, parfois même les générateurs ne suffisent pas. Dans ces cas, les fichiers mappés en mémoire peuvent être une bouée de sauvetage :

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class MyClass(metaclass=Singleton):
    pass

a = MyClass()
b = MyClass()
print(a is b)  # Output: True
Copier après la connexion

Cela vous permet de travailler avec des fichiers plus volumineux que votre RAM disponible, en chargeant uniquement les parties dont vous avez besoin en mémoire au fur et à mesure de vos besoins.

Enfin, parlons de quelques optimisations de mémoire spécifiques à Python. Saviez-vous que Python met en cache les petits entiers et les chaînes courtes ? Cela signifie que :

import weakref

class MyClass:
    def __init__(self, name):
        self.name = name

obj = MyClass("example")
weak_ref = weakref.ref(obj)

print(weak_ref())  # Output: <__main__.MyClass object at ...>
del obj
print(weak_ref())  # Output: None
Copier après la connexion
Copier après la connexion
Copier après la connexion

Ce stage peut économiser de la mémoire, mais attention à ne pas vous y fier pour des comparaisons d'égalité. Utilisez toujours == pour l'égalité, pas l'est.

En conclusion, la gestion de la mémoire de Python est un sujet profond et fascinant. En comprenant des concepts tels que les références faibles, le garbage collection cyclique et diverses techniques d'optimisation de la mémoire, vous pouvez écrire du code Python plus efficace et plus robuste. N'oubliez pas qu'une optimisation prématurée est la racine de tous les maux, alors établissez d'abord un profil et optimisez là où cela compte. Bon codage !


Nos créations

N'oubliez pas de consulter nos créations :

Centre des investisseurs | Vie intelligente | Époques & Échos | Mystères déroutants | Hindutva | Développeur Élite | Écoles JS


Nous sommes sur Medium

Tech Koala Insights | Epoques & Echos Monde | Support Central des Investisseurs | Mystères déroutants Medium | Sciences & Epoques Medium | Hindutva moderne

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal