Повторение результатов многопроцессорного списка занимает большие объемы памяти - PullRequest
0 голосов
/ 04 декабря 2018

У меня большой список.Я хочу обработать каждый элемент.Я хотел бы сегментировать список и обрабатывать каждый сегмент на другом процессоре.Я использую многопроцессорную библиотеку пафоса.Я создал следующую функцию:

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 ГБ памяти расходуется.

Ответы [ 2 ]

0 голосов
/ 04 декабря 2018

Некоторые дальнейшие испытания показывают, что травление не является проблемой.Обработка, которую я выполнял в for item in seg, создавала дополнительные объекты, которые занимали большой объем памяти.

Выводы, полученные из этого упражнения и интеллектуальных комментаторов:

  1. ProcessPoolметоды (map, imap, uimap) автоматически разбивают список на части.
  2. Если вы передаете большой объект в f (через замыкание), вы можете обнаружить, что этот список чанковывается вручную(как указано выше) экономит много ресурсов и увеличивает загрузку процессора.
  3. Использование imap и uimap может значительно сократить использование памяти.
0 голосов
/ 04 декабря 2018

Проблема с multiprocessing в том, что связь между процессами стоит дорого.Если ваш результат по размеру эквивалентен вводимому значению, вы, скорее всего, будете тратить большую часть своего времени на сбор и разбор данных, а не на что-либо полезное.Это зависит от того, насколько дорогой f, но вам лучше не использовать multiprocessing здесь.

...