Самый быстрый способ поделиться очень большой информацией между несколькими процессами без копирования - PullRequest
4 голосов
/ 09 февраля 2020

TL; DR: Как разделить большой (200 МБ) файл только для чтения между несколькими процессами с высокой производительностью, к которому ОЧЕНЬ тяжело получить доступ, при этом каждый процесс не имеет полной копии в памяти.

РЕДАКТИРОВАТЬ: Это Похоже, если я просто передам словарь в качестве аргумента для многопроцессорной обработки. Пул / Процесс фактически не создаст копию, если работник не изменит словарь. Я просто предполагал, что это скопирует. Такое поведение, похоже, Unix только там, где доступен форк, и даже не всегда. Но если это так, это должно решить мою проблему, пока это не преобразуется в задание ETL.

Что я пытаюсь сделать:

У меня есть задача улучшить сценарий, который реплицирует данные из один магазин в другой. Нормализация и преобразование данных в пути. Эта задача работает в масштабе около 100 миллионов документов, поступающих из хранилища исходных документов, которые свернуты и отправлены в другое хранилище документов назначения.

Каждый документ имеет идентификатор, и существует еще одно хранилище документов, которое по сути является хранилище значений ключей этих идентификаторов сопоставлено с некоторой дополнительной информацией, необходимой для этой задачи. Это хранилище намного меньше и делает запросы к нему, пока идет документ из основного хранилища, на самом деле не вариант без тяжелого кеширования, и этот тяжелый кеш очень быстро становится копией всего этого. Я просто создаю словарь из всего этого магазина в начале, прежде чем что-то запускать, и использую его. Этот словарь имеет размер около 200 МБ. Обратите внимание, что этот словарь читается только из.

Для этого я настроил многопроцессорность и имею около 30 одновременных процессов. Я поделил работу для каждого процесса таким образом, чтобы каждый работал по разным показателям и мог выполнить все это примерно за 4 часа.

Я заметил, что я сильно привязан к процессору, выполняя следующие 2 вещи:

  • Использование пула / потоков (что я сейчас делаю), чтобы каждый поток мог без проблем обращаться к dict. GIL убивает меня, и у меня один процесс работает на 100% все время, когда другие процессоры простаивают. Переключение на PyPy очень помогло, но я все еще не доволен этим подходом.
  • Создание Multiprocessing.Manager (). Dict () для большого dict и обеспечение доступа дочерних процессов через него. Процесс сервера, создаваемый этим подходом, постоянно выполняется на 100%. Я не знаю почему, поскольку я только читаю из этого словаря, поэтому сомневаюсь, что это запирается. Я не знаю, как работает Менеджер внутри, но я предполагаю, что дочерние процессы соединяются через Pipes / Sockets для каждой выборки, и накладные расходы это огромные. Это также предполагает, что использование Reddis / Memcache будет иметь ту же проблему, если это правда. Может быть, его можно настроить лучше?

Я связан с памятью, когда выполняю следующие действия:

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

I Сильно подозреваю, что если я что-то пропустил, мне просто придется «скачать больше оперативной памяти» или переписать из Python во что-то без GIL (или использовать ETL, как это должно быть сделано в ...).

В случае с оперативной памятью, каков наиболее эффективный способ хранения диктанта, подобного этому, чтобы укусить его меньше? В настоящее время это стандартный dict, сопоставленный с кортежем дополнительной информации, состоящей из 3 long / float.

doc_to_docinfo = {"ID1": (5.2, 3.0, 455),}

Есть ли есть ли более эффективные реализации hashmap для этого варианта использования, чем то, что я делаю?

1 Ответ

0 голосов
/ 28 марта 2020

У вас, похоже, есть похожая проблема, с которой я столкнулся. Можно использовать мой источник здесь , чтобы создать разбиение этих словарных ключей для потока. Мое предложение: разделить идентификаторы документов на разделы длиной 3 или 4, сохранить таблицу разделов в syn c для всех процессов / потоков, а затем просто переместить части ваших документов в каждый процесс / поток и в качестве точки входа для процесса поиск по словарю и выясняет, какой процесс может обрабатывать часть этого словаря. Если вы разбираетесь в балансировке разделов, у вас также может быть одинаковое количество документов на управляемый поток.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...