10
votes

Comment rédiger un programme de Python efficace en mémoire?

On dit que Python gère automatiquement la mémoire. Je suis confus parce que j'ai un programme Python utilise toujours plus de 2 Go de mémoire.

C'est un simple téléchargeur de données binaires multi-threads et déballeur. P>

MemTotal:        7975216 kB
MemFree:          732368 kB
Buffers:           38032 kB
Cached:          4365664 kB
SwapCached:        14016 kB
Active:          2182264 kB
Inactive:        4836612 kB


4 commentaires

La première question serait la suivante: combien de mémoire physique avez-vous disponible? Pourrait simplement être que Python décide qu'il ne vaut pas la peine de se nettoyer car il y a encore 1 Go de mémoire physique gratuite de toute façon.


Essayez Utilisation de la mémoire de profilage: Stackoverflow.com/questions/110259/python-Memory-Profiler


@Matthew, on dirait que 730 Mo de RAM de 8 Go sont disponibles.


Essayez de frayer 15 processus à la place - combien de mémoire consommez-vous de cette manière? Je devine beaucoup moins, et je suppose que le vrai coupable est le truc de threading (j'ai écrit un serveur TCP Echo très simple à l'aide de multithreading qui a pris 100% de CPU après 2 heures d'exécution - fixée avec torsadée, jamais la peine de regarder dans le filetage. manque de performance).


8 Réponses :


2
votes

Vous pouvez rendre ce programme plus efficace de la mémoire en ne lisant pas les 15 Mo de la connexion TCP, mais le traitement de chaque ligne telle qu'elle est lue. Cela permettra aux serveurs distants vous attendez, bien sûr, mais ça va.

Python n'est tout simplement pas très efficace de la mémoire. Il n'a pas été construit pour ça.


0 commentaires

5
votes

La dernière ligne devrait sûrement être f.close () ? Ces parens suivants sont un peu importants.


1 commentaires

Oui, c'est f.close (). C'était une faute de frappe en poste d'origine.



2
votes

Vous pouvez faire plus de votre travail dans le code C compilé si vous convertissez cela en une compréhension de la liste: xxx

à: xxx

Ceci est en fait légèrement différent de votre code d'origine. Dans votre version, GetData retourne un 3 tuple, qui revient dans des articles. Vous pouvez ensuite itérer sur ce triplet et appendez ';'. Joindre (article) pour chaque élément dedans. Cela signifie que vous obtenez 3 entrées ajoutées aux données de chaque triplet en lecture de GetData, chacun ';'. Join. Si les éléments ne sont que des chaînes, alors ';'. Joindre vous donnera une corde avec tous les autres personnages A '; - c'est ';'. Joindre ("ABC") redonnera "A; B; C". Je pense que ce que vous avez réellement voulait que chaque triplet soit enregistré à la liste des données comme les 3 valeurs du triplet, séparées par des points-virgules. C'est ce que ma version génère.

Cela peut également aider quelque peu avec votre problème de mémoire d'origine, car vous ne créez plus autant de valeurs Python. N'oubliez pas qu'une variable de python a beaucoup plus de frais généraux d'un dans une langue telle que C. Étant donné que chaque valeur est elle-même un objet et ajoutez la surcharge de chaque nom de référence à cet objet, vous pouvez facilement développer le Condition de stockage théorique plusieurs fois. Dans votre cas, la lecture de 15 Mo x 15 = 225Mb + la surcharge de chaque élément de chaque triple étant stockée sous forme d'une entrée de chaîne dans votre liste de données pourrait rapidement se développer à votre taille observée de 2 Go. Au minimum, ma version de votre liste de données n'aura que 1/3 des entrées, ainsi que les références d'élément distinctes sont ignorées, plus l'itération est effectuée dans le code compilé.


0 commentaires

6
votes

envisagez d'utiliser xRange () au lieu de la plage (), je crois que Xrange est un générateur tandis que la plage () étend toute la liste.

Je dirais soit de ne pas lire le fichier entier dans la mémoire, ou Don 'T Gardez toute la structure déballée en mémoire.

Actuellement, vous gardez à la fois en mémoire, en même temps, cela va être assez grand. Donc, vous avez donc au moins deux copies de vos données en mémoire, plus des métadonnées.

aussi la ligne finale xxx

peut vraiment dire que vous ". VE obtenu temporairement une troisième copie en mémoire (une grosse chaîne avec tout le fichier de sortie).

Donc, je dirais que vous le faites de manière assez inefficace, en conservant l'intégralité du fichier d'entrée, une sortie complète Fichier et une bonne quantité de données intermédiaires en mémoire à la fois.

Utiliser le générateur pour analyser, c'est une bonne idée. Envisagez d'écrire chaque enregistrement après que vous l'avez généré (il peut ensuite être jeté et la mémoire réutilisée), ou si cela provoque trop de demandes d'écriture, datez-les, disons, 100 rangées à la fois.

De même, la lecture de la réponse pourrait être faite en morceaux. Comme ils sont corrigés des enregistrements, cela devrait être raisonnablement facile.


0 commentaires

9
votes

Le principal coupable ici est tel que mentionné ci-dessus de l'appel de la plage (). Il créera une liste avec 15 millions de membres et cela mangera 200 MB de votre mémoire et avec 15 processus, c'est 3 Go.

Mais ne lisez pas non plus dans l'ensemble du fichier de 15 Mo dans des données (), de lecture bit à partir de la réponse. Coller ces 15 Mo dans une variable utilisera plus de 15 Mo de mémoire plus que de lire le bit de la réponse de la réponse.

Vous voudrez peut-être envisager simplement d'extraire simplement des données jusqu'à ce que vous épuisez si indata et de comparer le nombre de données que vous avez extraites avec ce que les premiers octets ont dit que cela devrait être. Ensuite, vous n'avez besoin ni gamme () ni xrange (). Semble plus pythonique pour moi. :)


2 commentaires

En fait, le nombre variable ici n'est pas de près de 15 millions de gamme car chaque morceau de données binaires est de 30 octets. Il ne créera donc pas une liste avec 15 millions d'éléments.


Ah, je vois. Pourtant, cela aidera autant que de ne pas lire dans toutes les données dans données , puis.



2
votes

Il y a 2 endroits évidents où vous gardez de gros objets de données en mémoire ( Data variable dans getdata () et data dans Mythead.run () - ces deux prendront environ 500 Mo) et il y a probablement d'autres endroits dans le code sauté. Il est à la fois facile de rendre la mémoire efficace. Utilisez réponse.read (4) au lieu de lire une réponse entière à la fois et faites-la de la même manière dans le code derrière Déballez la longueur fixe des données binaires ici . Changer data.append (...) dans mythread.run () à xxx

Ces modifications vous sauveront beaucoup de mémoire .


0 commentaires

1
votes

Assurez-vous de supprimer les threads après leur arrêt. (en utilisant del )


0 commentaires

11
votes

Comme les autres l'ont dit, vous avez besoin d'au moins les deux modifications suivantes:

  1. Ne crée pas une énorme liste d'entiers avec plage XXX

  2. ne crée pas de chaîne énorme en tant que corps de fichier complet à écrire à la fois XXX

    Encore meilleur, vous pouvez écrire le fichier comme: xxx


0 commentaires