2016-08-05 3 views
2

Si j'utilise multiprocessing.Array python pour créer un tableau partagé 1G, je trouve que le processus python utilise environ 30G de mémoire lors de l'appel à multiprocessing.Array, puis diminue l'utilisation de la mémoire après cette. J'apprécierais n'importe quelle aide pour comprendre pourquoi ceci se produit et pour contourner cela.python multiprocessing.Array: énorme surcharge de mémoire temporaire

Voici le code de le reproduire sous Linux, avec une mémoire contrôlée par smem:

import multiprocessing 
import ctypes 
import numpy 
import time 
import subprocess 
import sys 

def get_smem(secs,by): 
    for t in range(secs): 
     print subprocess.check_output("smem") 
     sys.stdout.flush() 
     time.sleep(by) 



def allocate_shared_array(n): 
    data=multiprocessing.Array(ctypes.c_ubyte,range(n)) 
    print "finished allocating" 
    sys.stdout.flush() 


n=10**9 
secs=30 
by=5 
p1=multiprocessing.Process(target=get_smem,args=(secs,by)) 
p2=multiprocessing.Process(target=allocate_shared_array,args=(n,)) 
p1.start() 
p2.start() 
print "pid of allocation process is",p2.pid 
p1.join() 
p2.join() 
p1.terminate() 
p2.terminate() 

Voici la sortie:

pid of allocation process is 2285 
    PID User  Command       Swap  USS  PSS  RSS 
2116 ubuntu top        0  700  773  1044 
1442 ubuntu -bash        0  2020  2020  2024 
1751 ubuntu -bash        0  2492  2528  2700 
2284 ubuntu python test.py      0  1080  4566 11924 
2286 ubuntu /usr/bin/python /usr/bin/sm  0  4688  5573  7152 
2276 ubuntu python test.py      0  4000  8163 16304 
2285 ubuntu python test.py      0 137948 141431 148700 

    PID User  Command       Swap  USS  PSS  RSS 
2116 ubuntu top        0  700  773  1044 
1442 ubuntu -bash        0  2020  2020  2024 
1751 ubuntu -bash        0  2492  2528  2700 
2284 ubuntu python test.py      0  1188  4682 12052 
2287 ubuntu /usr/bin/python /usr/bin/sm  0  4696  5560  7160 
2276 ubuntu python test.py      0  4016  8174 16304 
2285 ubuntu python test.py      0 13260064 13263536 13270752 

    PID User  Command       Swap  USS  PSS  RSS 
2116 ubuntu top        0  700  773  1044 
1442 ubuntu -bash        0  2020  2020  2024 
1751 ubuntu -bash        0  2492  2528  2700 
2284 ubuntu python test.py      0  1188  4682 12052 
2288 ubuntu /usr/bin/python /usr/bin/sm  0  4692  5556  7156 
2276 ubuntu python test.py      0  4016  8174 16304 
2285 ubuntu python test.py      0 21692488 21695960 21703176 

    PID User  Command       Swap  USS  PSS  RSS 
2116 ubuntu top        0  700  773  1044 
1442 ubuntu -bash        0  2020  2020  2024 
1751 ubuntu -bash        0  2492  2528  2700 
2284 ubuntu python test.py      0  1188  4682 12052 
2289 ubuntu /usr/bin/python /usr/bin/sm  0  4696  5560  7160 
2276 ubuntu python test.py      0  4016  8174 16304 
2285 ubuntu python test.py      0 30115144 30118616 30125832 

    PID User  Command       Swap  USS  PSS  RSS 
2116 ubuntu top        0  700  771  1044 
1442 ubuntu -bash        0  2020  2020  2024 
1751 ubuntu -bash        0  2492  2527  2700 
2284 ubuntu python test.py      0  1192  4808 12052 
2290 ubuntu /usr/bin/python /usr/bin/sm  0  4700  5481  7164 
2276 ubuntu python test.py      0  4092  8267 16304 
2285 ubuntu python test.py      0 31823696 31827043 31834136 

    PID User  Command       Swap  USS  PSS  RSS 
2116 ubuntu top        0  700  771  1044 
1442 ubuntu -bash        0  2020  2020  2024 
1751 ubuntu -bash        0  2492  2527  2700 
2284 ubuntu python test.py      0  1192  4808 12052 
2291 ubuntu /usr/bin/python /usr/bin/sm  0  4700  5481  7164 
2276 ubuntu python test.py      0  4092  8267 16304 
2285 ubuntu python test.py      0 31823696 31827043 31834136 

Process Process-2: 
Traceback (most recent call last): 
    File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap 
    self.run() 
    File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run 
    self._target(*self._args, **self._kwargs) 
    File "test.py", line 17, in allocate_shared_array 
    data=multiprocessing.Array(ctypes.c_ubyte,range(n)) 
    File "/usr/lib/python2.7/multiprocessing/__init__.py", line 260, in Array 
    return Array(typecode_or_type, size_or_initializer, **kwds) 
    File "/usr/lib/python2.7/multiprocessing/sharedctypes.py", line 115, in Array 
    obj = RawArray(typecode_or_type, size_or_initializer) 
    File "/usr/lib/python2.7/multiprocessing/sharedctypes.py", line 88, in RawArray 
    result = _new_value(type_) 
    File "/usr/lib/python2.7/multiprocessing/sharedctypes.py", line 63, in _new_value 
    wrapper = heap.BufferWrapper(size) 
    File "/usr/lib/python2.7/multiprocessing/heap.py", line 243, in __init__ 
    block = BufferWrapper._heap.malloc(size) 
    File "/usr/lib/python2.7/multiprocessing/heap.py", line 223, in malloc 
    (arena, start, stop) = self._malloc(size) 
    File "/usr/lib/python2.7/multiprocessing/heap.py", line 120, in _malloc 
    arena = Arena(length) 
    File "/usr/lib/python2.7/multiprocessing/heap.py", line 82, in __init__ 
    self.buffer = mmap.mmap(-1, size) 
error: [Errno 12] Cannot allocate memory 
+3

Si vous utilisez python 2, remplacez 'range (n)' par 'xrange (n)' –

Répondre

3

Depuis le format de vos déclarations d'impression, vous utilisez python 2 Remplacez range(n) par xrange(n) pour économiser de la mémoire.

data=multiprocessing.Array(ctypes.c_ubyte,xrange(n)) 

(ou utiliser Python 3)

1 milliards de gamme prend environ 8 Go (bien que je viens d'essayer que sur mon PC Windows et il a gelé: il suffit de ne pas faire ça!)

Essayé avec 10 ** 7 au lieu juste pour être sûr:

>>> z=range(int(10**7)) 
>>> sys.getsizeof(z) 
80000064 => 80 Megs! you do the math for 10**9 

une fonction de générateur comme xrange prend pas de mémoire car elle fournit les valeurs une par une quand itéré sur.

En Python 3, ils doivent avoir été nourris par ces problèmes, figurés que la plupart des gens utilisaient range parce qu'ils voulaient des générateurs, tués xrange et se sont range dans un générateur. Maintenant, si vous voulez vraiment allouer tous les numéros que vous avez à list(range(n)). Au moins, vous n'attribuez pas un téraoctet par erreur!

Edit:

Le commentaire OP signifie que mon explication ne résout pas le problème. J'ai fait quelques tests simples sur ma boîte de fenêtres:

import multiprocessing,sys,ctypes 
n=10**7 

a=multiprocessing.RawArray(ctypes.c_ubyte,range(n)) # or xrange 
z=input("hello") 

Ramps jusqu'à 500Mo reste alors à 250Mb avec python 2 Ramps jusqu'à 500Mo puis reste à 7Mb avec python 3 (ce qui est étrange car il devrait au moins 10Mb ...)

Conclusion: ok, ça pèse 500Mo, donc ce n'est pas sûr que cela aidera, mais pouvez-vous essayer votre programme sur Python 3 et voir si vous avez moins de pics de mémoire globale?

+1

Malheureusement, le problème n'est pas tellement lié à la portée, comme je viens de le montrer en guise d'illustration. En réalité, ces données seront lues à partir du disque. Je pourrais aussi utiliser n * ["a"] et spécifier c_char dans multiprocessing.Array comme autre exemple. Cela utilise encore environ 16G quand je n'ai que 1G de données dans la liste que je passe à multiprocessing.Array. Je me demande s'il y a du décapage inefficace ou quelque chose du genre. – jeffdiamond

1

Malheureusement, le problème n'est pas tellement avec la portée, comme je viens de le mettre dans une illustration simple. En réalité, ces données seront lues à partir du disque. Je pourrais aussi utiliser n * ["a"] et spécifier c_char dans multiprocessing.Array comme autre exemple. Cela utilise encore environ 16G quand je n'ai que 1G de données dans la liste que je passe à multiprocessing.Array. Je me demande s'il y a du décapage inefficace ou quelque chose du genre.

Je semble avoir trouvé une solution de contournement pour ce dont j'ai besoin en utilisant tempfile.SpooledTemporaryFile et numpy.memmap. Je peux ouvrir un mappage de la mémoire vers un fichier temporaire en mémoire, qui est spoulé sur le disque si nécessaire, et le partager entre différents processus en le passant en argument à multiprocessing.Process.

Je me demande toujours ce qui se passe avec le multiprocessing.Array cependant. Je ne sais pas pourquoi il utiliserait 16G pour un tableau de données 1G.

+0

Ok, tellement pour 'range'. Pourriez-vous poster un exemple reproductible et autonome alors? (bien, un MVCE comme on l'appelle ici) –

+0

J'ai testé plus. S'il vous plaît voir ma dernière édition –