Voici le programme:
#!/usr/bin/python
import multiprocessing
def dummy_func(r):
pass
def worker():
pass
if __name__ == '__main__':
pool = multiprocessing.Pool(processes=16)
for index in range(0,100000):
pool.apply_async(worker, callback=dummy_func)
# clean up
pool.close()
pool.join()
J'ai trouvé que l'utilisation de la mémoire (VIRT et RES) continuait de croître jusqu'à close ()/join (), y a-t-il une solution pour s'en débarrasser? J'ai essayé maxtasksperchild avec 2.7 mais cela n'a pas aidé non plus.
J'ai un programme plus compliqué qui appelle apply_async () ~ 6M fois, et à ~ 1,5M point j'ai déjà 6G + RES, pour éviter tous les autres facteurs, j'ai simplifié le programme à la version ci-dessus.
MODIFIER:
Il s'est avéré que cette version fonctionne mieux, merci pour la contribution de tout le monde:
#!/usr/bin/python
import multiprocessing
ready_list = []
def dummy_func(index):
global ready_list
ready_list.append(index)
def worker(index):
return index
if __name__ == '__main__':
pool = multiprocessing.Pool(processes=16)
result = {}
for index in range(0,1000000):
result[index] = (pool.apply_async(worker, (index,), callback=dummy_func))
for ready in ready_list:
result[ready].wait()
del result[ready]
ready_list = []
# clean up
pool.close()
pool.join()
Je n'y ai mis aucun verrou car je pense que le processus principal est monothread (le rappel est plus ou moins comme une chose événementielle par document que j'ai lu).
J'ai changé la plage d'index de v1 à 1000000, comme v2 et j'ai fait quelques tests - c'est bizarre pour moi, v2 est même ~ 10% plus rapide que v1 (33s contre 37s), peut-être que v1 faisait trop de travaux de maintenance de liste interne. v2 est définitivement un gagnant sur l'utilisation de la mémoire, il n'a jamais dépassé 300M (VIRT) et 50M (RES), alors que v1 était auparavant 370M/120M, le meilleur était 330M/85M. Tous les nombres n'étaient que 3 à 4 fois testés, référence seulement.
J'ai eu des problèmes de mémoire récemment, car j'utilisais plusieurs fois la fonction de multitraitement, donc elle continue de générer des processus et les laisse en mémoire.
Voici la solution que j'utilise maintenant:
def myParallelProcess(ahugearray)
from multiprocessing import Pool
from contextlib import closing
with closing( Pool(15) ) as p:
res = p.imap_unordered(simple_matching, ahugearray, 100)
return res
I ❤ avec
Utilisation map_async
au lieu de apply_async
pour éviter une utilisation excessive de la mémoire.
Pour votre premier exemple, modifiez les deux lignes suivantes:
for index in range(0,100000):
pool.apply_async(worker, callback=dummy_func)
à
pool.map_async(worker, range(100000), callback=dummy_func)
Il se terminera en un clin d'œil avant que vous puissiez voir son utilisation de la mémoire dans top
. Remplacez la liste par une plus grande pour voir la différence. Mais notez map_async
convertira d'abord l'itérable que vous lui passez en liste pour calculer sa longueur s'il n'a pas __len__
méthode. Si vous avez un itérateur d'un grand nombre d'éléments, vous pouvez utiliser itertools.islice
pour les traiter en petits morceaux.
J'ai eu un problème de mémoire dans un programme réel avec beaucoup plus de données et j'ai finalement trouvé que le coupable était apply_async
.
P.S., en ce qui concerne l'utilisation de la mémoire, vos deux exemples n'ont pas de différence évidente.
Créez simplement le pool dans votre boucle et fermez-le à la fin de la boucle avec pool.close()
.
J'ai un très grand ensemble de données de nuages de points 3D que je traite. J'ai essayé d'utiliser le module multitraitement pour accélérer le traitement, mais j'ai commencé à sortir des erreurs de mémoire. Après quelques recherches et tests, j'ai déterminé que je remplissais la file d'attente des tâches à traiter beaucoup plus rapidement que les sous-processus ne pouvaient la vider. Je suis sûr qu'en fragmentant, ou en utilisant map_async ou quelque chose, j'aurais pu ajuster la charge, mais je ne voulais pas apporter de changements majeurs à la logique environnante.
La solution idiote que j'ai trouvée consiste à vérifier le pool._cache
longueur par intermittence, et si le cache est trop volumineux, attendez que la file d'attente soit vide.
Dans ma boucle principale, j'avais déjà un compteur et un ticker d'état:
# Update status
count += 1
if count%10000 == 0:
sys.stdout.write('.')
if len(pool._cache) > 1e6:
print "waiting for cache to clear..."
last.wait() # Where last is assigned the latest ApplyResult
Donc, à chaque insertion de 10 000 dans le pool, je vérifie s'il y a plus d'un million d'opérations en file d'attente (environ 1 Go de mémoire utilisée dans le processus principal). Lorsque la file d'attente est pleine, j'attends juste que le dernier travail inséré se termine.
Maintenant, mon programme peut fonctionner pendant des heures sans manquer de mémoire. Le processus principal s'arrête de temps en temps pendant que les travailleurs continuent de traiter les données.
BTW le membre _cache est documenté l'exemple de pool de modules de multiprocessing:
#
# Check there are no outstanding tasks
#
assert not pool._cache, 'cache = %r' % pool._cache
Je pense que c'est similaire à la question que j'ai postée , mais je ne suis pas sûr que vous ayez le même délai. Mon problème était que je produisais des résultats à partir du pool de multitraitement plus rapidement que je ne les consommais, donc ils se sont accumulés en mémoire. Pour éviter cela, j'ai utilisé un sémaphore pour étrangler les entrées dans le pool afin qu'elles n'aillent pas trop loin devant les sorties que je consommais.