python multiprocessing.Array: огромные временные накладные расходы на память

Если я использую python multiprocessing.Array для создания общего массива 1G, я обнаружу, что процесс python использует около 30G памяти во время вызова multiprocessing.Array, а затем уменьшает использование памяти после этого. Я был бы признателен за любую помощь, чтобы выяснить, почему это происходит, и обойти это.

Вот код, чтобы воспроизвести его в Linux с памятью, отслеживаемой 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()

Вот вывод:

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

person jeffdiamond    schedule 05.08.2016    source источник
comment
если вы используете Python 2, замените range(n) на xrange(n)   -  person Jean-François Fabre    schedule 06.08.2016


Ответы (2)


Из формата ваших операторов печати вы используете python 2

Замените range(n) на xrange(n), чтобы сэкономить память.

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

(или используйте питон 3)

Диапазон в 1 миллиард занимает примерно 8 ГБ (ну, я только что попробовал это на своем ПК с Windows, и он завис: просто не делайте этого!)

Вместо этого попробовал 10 ** 7, чтобы быть уверенным:

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

Генераторная функция, такая как xrange, не требует памяти, так как предоставляет значения одно за другим при повторении.

В Python 3 они, должно быть, были сыты по горло этими проблемами, поняли, что большинство людей использовали range, потому что им нужны были генераторы, убили xrange и превратили range в генератор. Теперь, если вы действительно хотите выделить все номера, которые вам нужно, list(range(n)). По крайней мере, вы не выделяете один терабайт по ошибке!

Редактировать:

Комментарий OP означает, что мое объяснение не решает проблему. Я сделал несколько простых тестов на своем окне Windows:

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

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

Разгоняется до 500 МБ, затем остается на уровне 250 МБ с Python 2. Разгоняется до 500 МБ, затем остается на уровне 7 МБ с Python 3 (что странно, поскольку он должен быть не менее 10 МБ...)

Вывод: ок, максимальный объем памяти составляет 500 МБ, так что не уверен, что это поможет, но можете ли вы попробовать свою программу на Python 3 и посмотреть, меньше ли у вас общих пиков памяти?

person Jean-François Fabre    schedule 05.08.2016
comment
К сожалению, проблема не столько в дальности, как я просто привел это как простую иллюстрацию. На самом деле эти данные будут считываться с диска. Я также мог бы использовать n*[a] и указать c_char в multiprocessing.Array в качестве другого примера. Это все еще использует около 16 ГБ, когда у меня есть только 1 ГБ данных в списке, который я передаю в multiprocessing.Array. Мне интересно, происходит ли какое-то неэффективное травление или что-то в этом роде. - person jeffdiamond; 07.08.2016

К сожалению, проблема не столько в дальности, как я просто привел это как простую иллюстрацию. На самом деле эти данные будут считываться с диска. Я также мог бы использовать n*["a"] и указать c_char в multiprocessing.Array в качестве другого примера. Это все еще использует около 16 ГБ, когда у меня есть только 1 ГБ данных в списке, который я передаю в multiprocessing.Array. Мне интересно, происходит ли какое-то неэффективное травление или что-то в этом роде.

Кажется, я нашел обходной путь для того, что мне нужно, используя tempfile.SpooledTemporaryFile и numpy.memmap . Я могу открыть карту памяти для временного файла в памяти, который при необходимости буферизируется на диск, и разделить его между различными процессами, передав его в качестве аргумента в multiprocessing.Process.

Мне все еще интересно, что происходит с multiprocessing.Array. Я не знаю, почему он будет использовать 16G для массива данных 1G.

person jeffdiamond    schedule 06.08.2016
comment
Хорошо, так много для range. Не могли бы вы опубликовать воспроизводимый и автономный пример? (ну, MVCE, как его здесь называют) - person Jean-François Fabre; 07.08.2016