Какой лучший способ разделить большие файлы на Python для многопроцессорности?

Я сталкиваюсь со многими "смущающими параллельными" проектами, которые я хотел бы распараллелить с модулем multiprocessing. Тем не менее, они часто включают чтение в огромных файлах (более 2 гб), обработку их по строкам, запуск основных вычислений, а затем запись результатов. Каков наилучший способ разделить файл и обработать его с помощью модуля многопроцессорности Python? Должны использоваться Queue или JoinableQueue в multiprocessing? Или сам модуль Queue? Или, должен ли я сопоставлять файл итерабельным по пулу процессов с помощью multiprocessing? Я экспериментировал с этими подходами, но накладные расходы огромны в распределении данных по строкам. Я остановился на конструкции легких фильтров, используя cat file | process1 --out-file out1 --num-processes 2 | process2 --out-file out2, который передает определенный процент от первого ввода процесса непосредственно ко второму входу (см. этот пост), но я хотел бы иметь решение, содержащееся полностью в Python.

Удивительно, но документация на Python не предлагает канонического способа сделать это (несмотря на длительный раздел руководства по программированию в документации multiprocessing).

Спасибо, Vince

Дополнительная информация: Время обработки на строку варьируется. Некоторые проблемы бывают быстрыми и едва ли связаны с I/O, некоторые связаны с ЦП. Связанные с процессором, не зависящие от задачи задачи получат сообщение от распараллеливания, так что даже неэффективные способы присвоения данных функции обработки все равно будут полезны с точки зрения времени настенных часов.

Первым примером является script, который извлекает поля из строк, проверяет множество поразрядных флагов и записывает строки с определенными флагами в новый файл в совершенно новом формате. Это похоже на проблему с привязкой к I/O, но когда я запускал ее с моей дешевой параллельной версией с помощью труб, она была примерно на 20% быстрее. Когда я запускаю его с пулом и картой, или в очереди в multiprocessing, он всегда на 100% медленнее.

Ответы

Ответ 1

Одна из лучших архитектур уже является частью ОС Linux. Никаких специальных библиотек не требуется.

Вы хотите создать "вентиляторный дизайн".

  • "Основная" программа создает несколько подпроцессов, связанных между собой каналами.

  • Основная программа считывает файл, записывая строки в трубы, выполняющие минимальную фильтрацию, необходимую для обработки строк соответствующим подпроцессам.

Каждый подпроцесс, вероятно, должен быть конвейером различных процессов, которые читаются и записываются из stdin.

Вам не нужна структура данных очереди, именно то, что конвейер в памяти - очередь байтов между двумя параллельными процессами.

Ответ 2

Одна стратегия состоит в том, чтобы назначить каждому работнику смещение, так что если у вас есть восемь рабочих процессов, которые вы назначаете, то числа от 0 до 7. Рабочий номер 0 считывает первые процессы записи, а затем пропускает 7 и продолжает обрабатывать 8-ю запись и т. рабочий номер 1 читает вторую запись, а затем пропускает 7 и обрабатывает 9-ю запись.........

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

Ответ 3

Вы не упоминаете, как обрабатываете строки; возможно, самая важная часть информации.

Является ли каждая строка независимой? Является ли расчет зависящим от одной строки до следующего? Должны ли они обрабатываться в блоках? Сколько времени занимает обработка для каждой линии? Есть ли этап обработки, который должен включать "все" данные в конце? Или можно отбросить промежуточные результаты и сохранить только текущую сумму? Может ли файл быть первоначально разделен путем деления размера файла на количество потоков? Или он растет, когда вы его обрабатываете?

Если строки независимы и файл не растет, единственная координация, в которой вы нуждаетесь, заключается в том, чтобы обрабатывать "начальные адреса" и "длины" для каждого из рабочих; они могут самостоятельно открывать и искать в файле, а затем вы должны просто координировать свои результаты; возможно, ожидая, что N результатов вернутся в очередь.

Если строки не являются независимыми, ответ будет сильно зависеть от структуры файла.

Ответ 4

Это зависит от формата файла.

Есть ли смысл разделить его где угодно? Или вам нужно разбить его по новой строке? Или вам нужно убедиться, что вы разделили его в конце определения объекта?

Вместо разделения файла вы должны использовать несколько считывателей в одном файле, используя os.lseek, чтобы перейти к соответствующей части файла.

Обновление: плакат добавил, что он хочет разбить на новые строки. Затем я предлагаю следующее:

Скажем, у вас 4 процесса. Тогда простое решение - os.lseek до 0%, 25%, 50% и 75% файла и чтение байтов, пока вы не нажмете первую новую строку. Это ваша отправная точка для каждого процесса. Для этого вам не нужно разделить файл, просто найдите нужное место в большом файле в каждом процессе и начните читать оттуда.

Ответ 5

Я знаю, что вы специально задали вопрос о Python, но я попрошу вас взглянуть на Hadoop (http://hadoop.apache.org/): он реализует карту и уменьшить алгоритм, который был специально разработан для решения этой проблемы.

Удачи.

Ответ 6

Fredrik Lundh Некоторые заметки о Тим Брей Широкий поиск Бенчмарк - это интересное чтение, о очень похожем случае, с большим количеством полезных совет. Различные другие авторы также реализовали одно и то же, некоторые из них связаны с этой статьей, но вы можете попробовать выполнить поиск в googling для "python wide finder" или что-то еще. (там также было решение, основанное на модуле multiprocessing, но похоже, что оно больше не доступно)

Ответ 7

Если время выполнения длинное, вместо того, чтобы каждый процесс считывал следующую строку через Queue, процессы обрабатывают партии строк. Таким образом, накладные расходы амортизируются в течение нескольких строк (например, тысяч или более).