Многопроцессорность Python3: ошибка выделения памяти

Я знаю, что этот вопрос задавался много раз, но ответы не применимы. Это ответ на один параллельный цикл с использованием многопроцессорной обработки в StackoverFlow:

import multiprocessing as mp

def processInput(i):
    return i * i

if __name__ == '__main__':
    inputs = range(1000000)
    pool = mp.Pool(processes=4)
    results = pool.map(processInput, inputs)
    print(results)

Этот код работает нормально. Но если я увеличу диапазон до 1000000000, мои 16 ГБ ОЗУ будут полностью заполнены, и я получу сообщение [Errno 12] Не удается выделить память. Кажется, что функция map запускает как можно больше процессов. Как ограничить количество параллельных процессов?


person J. Doe    schedule 25.09.2017    source источник


Ответы (2)


Функция pool.map запускает 4 процесса, как вы ей указали (в строке process=4 вы указываете пулу, сколько процессов он может использовать для выполнения вашей логики).

Однако в основе этой реализации лежит другая проблема. Функция pool.map вернет список объектов, в данном случае их номера. Числа не действуют как int-s в ANSI-C, они имеют накладные расходы и не будут переполняться (например, превращаться в -2 ^ 31 при достижении 2 ^ 31 + 1 на 32-разрядных). Кроме того, списки Python не являются массивом и несут накладные расходы.

Чтобы быть более конкретным, на python 3.6 запуск следующего кода покажет некоторые накладные расходы:

>>>import sys
>>>t = [1,2,3,4]
>>>sys.getsizeof(t)
96
>>>t = [x for x in range(1000)]
>>>sys.getsizeof(t)
9024

Таким образом, это означает 24 байта на число в маленьких списках и ~9 байтов в больших списках. Таким образом, для списка размером 10 ^ 9 мы получаем около 8,5 ГБ.

РЕДАКТИРОВАТЬ: 1. Как уже упоминалось в tfb, это даже не размер базовых объектов Number, а просто указатели и накладные расходы на список, что означает гораздо больше накладных расходов на память, которые я не учел в исходном ответе.

  1. Установка python по умолчанию в Windows является 32-разрядной (вы можете получить 64-разрядную установку, но вам нужно проверить раздел всех доступных загрузок на веб-сайте python), поэтому я предположил, что вы используете 32-разрядную установку.
person Fanchi    schedule 22.10.2017
comment
Я почти уверен, что sys.getsizeof просто сообщает вам размер объекта массива, а не его содержимое. Например, 1.0*sys.getsizeof([[i for i in xrange(10)] for j in xrange(10000)])/10000 в моей реализации составляет около 8,7. Так что ясно, что это не подсчет объектов в массиве, а только указатели на них. И sys.getsizeof(1) равно 24. (Питон, похоже, не делает ничего умного с тегами, поэтому, насколько я вижу, fixnums может быть немедленным.) - person ; 22.10.2017

range(1000000000) создает список из 10^9 ints. Это около 8 ГБ (8 байт на int в 64-битной системе). Затем вы пытаетесь обработать это, чтобы создать другой список из 10 ^ 9 ints. Действительно очень умная реализация могла бы сделать это на машине с 16 ГБ, но в основном это безнадежное дело.

В Python 2 вы можете попробовать использовать xrange, что может помочь, а может и нет. Я не уверен, что такое эквивалент Python 3.

person Community    schedule 25.09.2017
comment
Python 3 устарел от реализации диапазона для генератора xrange, range() в python 3 эквивалентен использованию xrange() в python 2. - person Fanchi; 22.10.2017
comment
@Фанчи Спасибо. Я, вероятно, удалю этот ответ, потому что я думаю, что ваш лучше. - person ; 22.10.2017