Я хотел бы распараллелить мою программу Python, чтобы она могла использовать несколько процессоров на машине, на которой она работает. Мое распараллеливание очень простое, поскольку все параллельные «потоки» программы независимы и записывают свой вывод в отдельные файлы. Мне не нужны потоки для обмена информацией, но я обязательно знаю, когда потоки завершаются, поскольку некоторые шаги моего конвейера зависят от их вывода.
Переносимость важна, поскольку я хотел бы, чтобы это работало на любой версии Python на Mac, Linux и Windows. С учетом этих ограничений, какой модуль Python является наиболее подходящим для реализации этого? Я пытаюсь выбрать между потоком, подпроцессом и многопроцессорностью, которые, похоже, предоставляют связанные функции.
Есть мысли по этому поводу? Мне нужно самое простое портативное решение.
источник
Ответы:
multiprocessing
отличный швейцарский армейский ножевой модуль. Он более общий, чем потоки, так как вы даже можете выполнять удаленные вычисления. Поэтому я предлагаю вам использовать этот модуль.Этот
subprocess
модуль также позволит вам запускать несколько процессов, но я обнаружил, что он менее удобен в использовании, чем новый модуль многопроцессорности.Потоки, как известно, тонкие, и с CPython вы часто ограничены одним ядром с ними (хотя, как отмечено в одном из комментариев, глобальная блокировка интерпретатора (GIL) может быть выпущена в коде C, вызываемом из кода Python) .
Я считаю, что большинство функций трех модулей, которые вы цитируете, можно использовать независимо от платформы. Что
multiprocessing
касается переносимости, обратите внимание, что он входит в стандартную комплектацию только начиная с Python 2.6 (хотя версия для некоторых более старых версий Python существует). Но это отличный модуль!источник
Для меня это на самом деле довольно просто:
Вариант подпроцесса :
subprocess
предназначен для запуска других исполняемых файлов --- это в основном оболочка вокругos.fork()
иos.execve()
с некоторой поддержкой для необязательного подключения (настройка PIPE для подпроцессов и обратно. Очевидно, что вы можете использовать другие механизмы межпроцессного взаимодействия (IPC), такие как сокеты или Posix или Общая память SysV. Но вы будете ограничены любыми интерфейсами и каналами IPC, которые поддерживаются программами, которые вы вызываете.Обычно используется любая
subprocess
синхронная функция - просто вызывая некоторую внешнюю утилиту и считывая ее вывод или ожидая ее завершения (возможно, считывая ее результаты из временного файла или после того, как он отправил их в некоторую базу данных).Однако можно создать сотни подпроцессов и опросить их. Мой любимый служебный класс делает именно это. Самый большой недостаток в
subprocess
модуле является то , что поддержка ввода / вывода , как правило блокировки. Существует черновик PEP-3145, чтобы исправить это в какой-то будущей версии Python 3.x, и альтернативный asyncproc (предупреждение, которое ведет прямо к загрузке, а не к какой-либо документации или README). Я также обнаружил, что относительно легко просто импортировать файловые дескрипторы PIPEfcntl
и управлять имиPopen
напрямую, хотя я не знаю, переносится ли это на платформы, отличные от UNIX.(Обновление: 7 августа 2019 г .: поддержка Python 3 для подпроцессов ayncio : подпроцессы asyncio )
subprocess
почти не имеет поддержки обработки событий ... хотя вы можете использоватьsignal
модуль и простые сигналы UNIX / Linux старой школы - как бы мягко убивая ваши процессы.Вариант многопроцессорности :
multiprocessing
предназначен для выполнения функций в существующем (Python) коде с поддержкой более гибкого взаимодействия между этим семейством процессов. В частности, лучше всего строить свойmultiprocessing
IPC вокругQueue
объектов модуля, где это возможно, но вы также можете использоватьEvent
объекты и различные другие функции (некоторые из которых, предположительно, построены наmmap
поддержке платформ, на которых этой поддержки достаточно).multiprocessing
Модуль Python предназначен для предоставления интерфейсов и функций, которые очень похожи наthreading
то, что позволяет CPython масштабировать вашу обработку между несколькими процессорами / ядрами, несмотря на GIL (Global Interpreter Lock). Он использует все усилия по детальной блокировке SMP и согласованности, которые были предприняты разработчиками ядра вашей ОС.Вариант заправки :
threading
предназначен для довольно узкого диапазона приложений, которые связаны с вводом-выводом (не нужно масштабировать на несколько ядер ЦП) и которые выигрывают от чрезвычайно низкой задержки и накладных расходов на переключение при переключении потоков (с общей памятью ядра) по сравнению с процессом / переключение контекста. В Linux это почти пустой набор (время переключения процессов Linux очень близко к его переключателям потоков).threading
у Python есть два основных недостатка .Один из них, конечно, зависит от реализации - в основном затрагивает CPython. Это GIL. По большей части, большинство программ CPython не выиграют от доступности более двух процессоров (ядер), и часто производительность будет снижаться из-за блокировки GIL.
Более серьезная проблема, не зависящая от реализации, заключается в том, что потоки совместно используют одну и ту же память, обработчики сигналов, дескрипторы файлов и некоторые другие ресурсы ОС. Таким образом, программист должен быть чрезвычайно осторожен с блокировкой объектов, обработкой исключений и другими аспектами своего кода, которые являются тонкими и могут убить, остановить или заблокировать весь процесс (набор потоков).
Для сравнения,
multiprocessing
модель предоставляет каждому процессу свою собственную память, файловые дескрипторы и т. Д. Сбой или необработанное исключение в любом из них приведет только к уничтожению этого ресурса, и надежная обработка исчезновения дочернего или родственного процесса может быть значительно проще, чем отладка, изоляция и исправление или обход аналогичных проблем в потоках.threading
с основными системами Python, такими как NumPy , может значительно меньше пострадать от конкуренции с GIL, чем большая часть вашего собственного кода Python. Это потому, что они были специально разработаны для этого; собственные / двоичные части NumPy, например, выпустит GIL, когда это безопасно).Скрученный вариант:
Также стоит отметить, что Twisted предлагает еще одну альтернативу, которая одновременно элегантна и очень сложна для понимания . По сути, рискуя чрезмерным упрощением до такой степени, что фанаты Twisted могут штурмовать мой дом с вилами и факелами, Twisted обеспечивает управляемую событиями кооперативную многозадачность в рамках любого (одного) процесса.
Чтобы понять, как это возможно, следует прочитать об особенностях
select()
(которые могут быть построены на основе select () или poll () или аналогичных системных вызовов ОС). По сути, все это связано с возможностью сделать запрос ОС в спящий режим в ожидании любой активности в списке файловых дескрипторов или некоторого тайм-аута.Пробуждение от каждого из этих вызовов
select()
является событием: либо вход, доступный (читаемый) на некотором количестве сокетов или файловых дескрипторов, либо доступное пространство буферизации на некоторых других (записываемых) дескрипторах или сокетах, некоторые исключительные условия (TCP внеполосные PUSH-пакеты, например) или TIMEOUT.Таким образом, модель программирования Twisted построена на обработке этих событий и последующем зацикливании результирующего «основного» обработчика, что позволяет ему отправлять события вашим обработчикам.
Я лично считаю, что название Twisted напоминает модель программирования ... поскольку ваш подход к проблеме должен быть в каком-то смысле "вывернут" наизнанку. Вместо того, чтобы рассматривать вашу программу как серию операций с входными данными и выходами или результатами, вы пишете свою программу как службу или демон и определяете, как она реагирует на различные события. (Фактически, основной «основной цикл» программы Twisted - это (обычно? Всегда?) А
reactor()
).В основных вызовы с использованием Twisted включает скручивания ума вокруг управляемых событий модели , а также сторониться использование любых библиотек классов или наборов инструментальных средств , которые не писаны сотрудничать в Twisted рамок. Вот почему Twisted предоставляет свои собственные модули для обработки протокола SSH, для curses и свои собственные функции subprocess / Popen, а также многие другие модули и обработчики протоколов, которые, на первый взгляд, могут дублировать элементы стандартных библиотек Python.
Я думаю, что понимать Twisted на концептуальном уровне полезно, даже если вы никогда не собираетесь его использовать. Это может дать представление о производительности, конкуренции и обработке событий в вашей потоковой, многопроцессорной и даже подпроцессной обработке, а также о любой выполняемой вами распределенной обработке.
( Примечание. Новые версии Python 3.x включают функции asyncio (асинхронный ввод-вывод), такие как async def , декоратор @ async.coroutine и ключевое слово await , а также yield from future support. Все они примерно похожи на Искажено с точки зрения процесса (кооперативная многозадачность). (Текущий статус поддержки Twisted для Python 3 см .: https://twistedmatrix.com/documents/current/core/howto/python3.html )
Распределенный вариант:
Еще одна область обработки, о которой вы не спрашивали, но которую стоит рассмотреть, - это распределенная обработка. Существует множество инструментов и сред Python для распределенной обработки и параллельных вычислений. Лично я считаю, что проще всего использовать тот, который реже всего считается находящимся в этом пространстве.
Построить распределенную обработку на основе Redis - почти тривиально . Все хранилище ключей может использоваться для хранения рабочих единиц и результатов, списки Redis LIST могут использоваться как
Queue()
подобные объекты, а поддержка PUB / SUB может использоваться дляEvent
-подобной обработки. Вы можете хэшировать свои ключи и использовать значения, реплицированные в свободном кластере экземпляров Redis, для хранения топологии и сопоставлений хэш-токенов, чтобы обеспечить согласованное хеширование и переключение при отказе для масштабирования за пределами возможностей любого отдельного экземпляра для координации ваших рабочих и данные маршалинга (маринованные, JSON, BSON или YAML) среди них.Конечно , как вы начинаете строить большие масштабы и более сложные решения вокруг Redis вы повторно реализации многих функций , которые уже были решены с помощью, сельдерей , Apache Спарк и Hadoop , Zookeeper , etcd , Cassandra и так далее. У всех есть модули для доступа Python к их сервисам.
[Обновление: пара ресурсов для рассмотрения, если вы рассматриваете Python для ресурсоемких вычислений в распределенных системах: IPython Parallel и PySpark . Хотя это распределенные вычислительные системы общего назначения, они являются особенно доступными и популярными подсистемами науки о данных и аналитики].
Вывод
Здесь у вас есть спектр альтернатив обработки для Python, от однопоточной обработки с простыми синхронными вызовами подпроцессов, пулов опрашиваемых подпроцессов, многопоточной и многопроцессорной обработки, совместной многозадачности, управляемой событиями, и до распределенной обработки.
источник
В подобном случае я выбрал отдельные процессы и небольшую часть необходимой связи через сетевой сокет. Он очень портативен и довольно прост в использовании с помощью python, но, вероятно, не проще (в моем случае у меня было еще одно ограничение: связь с другими процессами, написанными на C ++).
В вашем случае я бы, вероятно, пошел на многопроцессорность, поскольку потоки python, по крайней мере, при использовании CPython, не являются настоящими потоками. Что ж, это собственные системные потоки, но модули C, вызываемые из Python, могут или не могут выпускать GIL и разрешать другим потокам их запускать при вызове кода блокировки.
источник
Чтобы использовать несколько процессоров в CPython, ваш единственный выбор - это
multiprocessing
модуль. CPython блокирует свои внутренние компоненты ( GIL ), что предотвращает параллельную работу потоков на других процессорах.multiprocessing
Модуль создает новые процессы (какsubprocess
) и управляет связью между ними.источник
Отключитесь и позвольте unix делать вашу работу:
используйте iterpipes для переноса подпроцесса, а затем:
С сайта Теда Зюбы
INPUTS_FROM_YOU | xargs -n1 -0 -P ЧИСЛО / процесс # ЧИСЛО параллельных процессов
ИЛИ
Gnu Parallel также будет обслуживать
Вы тусуетесь с GIL, пока отправляете закулисных мальчиков выполнять свою многоядерную работу.
источник