web-dev-qa-db-fra.com

Utilisation élevée de la mémoire à l'aide de Python Multiprocessing

J'ai vu quelques articles sur l'utilisation de la mémoire en utilisant Python Module multiprocessing. Cependant, les questions ne semblent pas répondre au problème que j'ai ici. Je publie mon analyse avec l'espoir que quelqu'un peut m'aider.

Problème

J'utilise le multitraitement pour effectuer des tâches en parallèle et j'ai remarqué que la consommation de mémoire par les processus de travail augmente indéfiniment. J'ai un petit exemple autonome qui devrait reproduire ce que je remarque.

import multiprocessing as mp
import time

def calculate(num):
    l = [num*num for num in range(num)]
    s = sum(l)
    del l       # delete lists as an  option
    return s

if __== "__main__":
    pool = mp.Pool(processes=2)
    time.sleep(5)
    print "launching calculation"
    num_tasks = 1000
    tasks =  [pool.apply_async(calculate,(i,)) for i in range(num_tasks)]
    for f in tasks:    
        print f.get(5)
    print "calculation finished"
    time.sleep(10)
    print "closing  pool"
    pool.close()
    print "closed pool"
    print "joining pool"
    pool.join()
    print "joined pool"
    time.sleep(5)

Système

J'utilise Windows et j'utilise le gestionnaire de tâches pour surveiller l'utilisation de la mémoire. J'exécute Python 2.7.6.

Observation

J'ai résumé la consommation de mémoire par les 2 processus de travail ci-dessous.

+---------------+----------------------+----------------------+
|  num_tasks    |  memory with del     | memory without del   |
|               | proc_1   | proc_2    | proc_1   | proc_2    |
+---------------+----------------------+----------------------+
| 1000          | 4884     | 4694      | 4892     | 4952      |
| 5000          | 5588     | 5596      | 6140     | 6268      |
| 10000         | 6528     | 6580      | 6640     | 6644      |
+---------------+----------------------+----------------------+

Dans le tableau ci-dessus, j'ai essayé de changer le nombre de tâches et d'observer la mémoire consommée à la fin de tout calcul et avant join- _ pool. Les options 'del' et 'without del' permettent de supprimer ou de commenter la ligne del l À l'intérieur de la fonction calculate(num) respectivement. Avant calcul, la consommation mémoire est d'environ 4400.

  1. Il semble que l'effacement manuel des listes entraîne une utilisation moindre de la mémoire pour les processus de travail. Je pensais que le ramasse-miettes se serait occupé de ça. Existe-t-il un moyen de forcer la collecte des ordures?
  2. Il est déconcertant qu'avec l'augmentation du nombre de tâches, l'utilisation de la mémoire continue de croître dans les deux cas. Existe-t-il un moyen de limiter l'utilisation de la mémoire?

J'ai un processus qui est basé sur cet exemple et est destiné à fonctionner à long terme. J'observe que ce processus de travail accapare beaucoup de mémoire (~ 4 Go) après une course de nuit. Faire un join pour libérer de la mémoire n'est pas une option et j'essaie de trouver un moyen sans join-.

Cela semble un peu mystérieux. Quelqu'un a-t-il rencontré quelque chose de similaire? Comment puis-je résoudre ce problème?

36
Goutham

J'ai fait beaucoup de recherches et je n'ai pas trouvé de solution pour résoudre le problème en soi. Mais il existe un travail décent qui empêche l'éclatement de la mémoire pour un petit coût, en particulier du côté du code à exécution longue côté serveur.

La solution consistait essentiellement à redémarrer les processus de travail individuels après un nombre fixe de tâches. La classe Pool dans python prend maxtasksperchild comme argument. Vous pouvez spécifier maxtasksperchild=1000 limitant ainsi 1 000 tâches à exécuter sur chaque processus enfant. Après avoir atteint le nombre maxtasksperchild, le pool actualise ses processus enfants. En utilisant un nombre prudent pour les tâches maximales, on peut équilibrer la mémoire maximale consommée, avec le coût de démarrage associé au redémarrage du processus principal. La construction Pool se fait comme suit:

pool = mp.Pool(processes=2,maxtasksperchild=1000)

Je mets ici ma solution complète pour qu'elle puisse être utile aux autres!

import multiprocessing as mp
import time

def calculate(num):
    l = [num*num for num in range(num)]
    s = sum(l)
    del l       # delete lists as an  option
    return s

if __== "__main__":

    # fix is in the following line #
    pool = mp.Pool(processes=2,maxtasksperchild=1000)

    time.sleep(5)
    print "launching calculation"
    num_tasks = 1000
    tasks =  [pool.apply_async(calculate,(i,)) for i in range(num_tasks)]
    for f in tasks:    
        print f.get(5)
    print "calculation finished"
    time.sleep(10)
    print "closing  pool"
    pool.close()
    print "closed pool"
    print "joining pool"
    pool.join()
    print "joined pool"
    time.sleep(5)
61
Goutham