У меня большой список.Я хочу обработать каждый элемент.Я хотел бы сегментировать список и обрабатывать каждый сегмент на другом процессоре.Я использую многопроцессорную библиотеку пафоса.Я создал следующую функцию:
def map_list_in_segments (l, f):
cpus = max(1, int(cpu_count() / 2) - 1)
seg_length = int(len(l) / cpus)
segments = [l[x:x+seg_length] for x in range(0,len(l),seg_length)]
pool = Pool(nodes=cpus)
mapped_segments = pool.map(lambda seg: f(seg), segments)
return (sg for seg in mapped_segments for sg in seg)
Она возвращает правильный результат и использует все (или почти все) процессоры.Однако итерация по возвращенному списку приводит к неожиданному использованию очень большого количества памяти.
Сначала я возвращал понимание списка.Я переключил это на генератор, надеясь на меньшее потребление памяти, но это ничего не улучшило.
Обновление на основе комментариев:
Я не знал о imap
и uimap
и что они автоматически разделяют список ввода.Я попробовал uimap
, но увидел очень низкую загрузку процессора и очень длительное время работы.Один из процессов имел очень высокую загрузку ЦП.Я думаю, что происходит много травления.У f
, который я передаю, есть большой объект в закрытии.При использовании методов ProcessingPool (map
, imap
, uimap
) этот объект необходимо выбрать для каждого элемента в списке.Я подозреваю, что это то, что делает один очень занятый процесс.Этот процесс блокирует другие процессы.
Если это так, это объясняет, почему мое ручное сегментирование приводит к значительному увеличению загрузки ЦП: крупный объект нужно травить только один раз на сегмент, а не на каждый элемент.
Затем я попытался использовать uimap
в моем map_list_in_segments
, надеясь на снижение потребления памяти, но этого не произошло.Вот как выглядит код, вызывающий метод и повторяющий результаты:
segments = multiprocessing.map_list_in_segments(l, lambda seg: process_segment(seg, large_object_needed_for_processing))
for seg in segments:
for item in seg:
# do something with item
Мое (ограниченное) понимание генераторов состоит в том, что первый цикл for
, который проходит по сегментам, должен освобождать каждый из памятикак это повторяется.Если это так, то может показаться, что большое использование памяти - это получение возвращаемых значений метода process_segment
.Я не возвращаю большие объемы данных (около 1 Кбайт для каждого элемента), а размер l
, с которым я работаю, составляет 6000 элементов.Не уверен, почему 5 ГБ памяти расходуется.