Как распараллелить вычисления со списком в Python? - PullRequest
40 голосов
/ 08 марта 2011

Как составления списков, так и вычисления на карте должны, по крайней мере в теории, быть сравнительно простыми для параллелизации: каждое вычисление внутри составления списка может выполняться независимо от вычислений всех других элементов. Например, в выражении

[ x*x for x in range(1000) ]

каждый х * х-расчет можно (по крайней мере, теоретически) сделать параллельно.

У меня такой вопрос: есть ли какой-нибудь Python-Module / Python-Реализация / Python Programming-Trick для распараллеливания вычисления со списком (для использования всех 16/32 / ... ядер или для распределения вычислений по компьютеру -Сетка или над облаком)?

Ответы [ 8 ]

30 голосов
/ 08 марта 2011

Как сказал Кен, это невозможно, но с помощью многопроцессорного модуля версии 2.6 * очень просто распараллелить вычисления.

import multiprocessing

try:
    cpus = multiprocessing.cpu_count()
except NotImplementedError:
    cpus = 2   # arbitrary default


def square(n):
    return n * n

pool = multiprocessing.Pool(processes=cpus)
print(pool.map(square, range(1000)))

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

8 голосов
/ 09 марта 2011

При автоматическом распараллеливании понимания списка

ИМХО, в силе автоматическая парализация паролей понимания списка была бы невозможна без дополнительной информации (например, предоставляемой с использованием директив в OpenMP) или ее ограничения довыражения, которые включают только встроенные типы / методы.

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

# Artificial example
counter = 0

def g(x): # func with side-effect
    global counter
    counter = counter + 1
    return x + counter

vals = [g(i) for i in range(100)] # diff result when not done in order

Существует также проблема распределения задач.Как должно быть разложено проблемное пространство?

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

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

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

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

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

Альтернативы

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

ИмеяЯ использовал только MPI (в C) без опыта использования Python для параллельной обработки, я не могу поручиться за любого (хотя, при быстром сканировании, многопроцессорная , кувшин , pp и pyro выделяются).

Если требуется придерживаться как можно ближе к списку понимания, то jug кажетсябудь ближе всехИз учебника распределение задач по нескольким экземплярам может быть простым:

from jug.task import Task
from yourmodule import process_data
tasks = [Task(process_data,infile) for infile in glob('*.dat')]

В то время как это делает что-то похожее на multiprocessing.Pool.map(), jug может использовать разные серверные части для синхронизации процессаи хранение промежуточных результатов (redis, filesystem, in-memory), что означает, что процессы могут охватывать узлы в кластере.

5 голосов
/ 09 марта 2011

Использование функций futures.{Thread,Process}PoolExecutor.map(func, *iterables, timeout=None) и futures.as_completed(future_instances, timeout=None) из нового пакета 3.2 concurrent.futures может помочь.

Это также доступно как 2,6+ backport .

4 голосов
/ 02 июня 2011

Для параллелизма с общей памятью я рекомендую joblib :

from joblib import delayed, Parallel

def square(x): return x*x
values = Parallel(n_jobs=NUM_CPUS)(delayed(square)(x) for x in range(1000))
3 голосов
/ 08 марта 2011

Нет, потому что само понимание списка является своего рода C-оптимизированным макросом. Если вы вытащите его и распараллелите, то это не понимание списка, это просто старый добрый MapReduce .

Но вы можете легко распараллелить свой пример. Вот хорошее руководство по использованию MapReduce с библиотекой параллелизации Python:

http://mikecvet.wordpress.com/2010/07/02/parallel-mapreduce-in-python/

1 голос
/ 07 февраля 2019

Как показывают приведенные выше ответы, на самом деле это довольно сложно сделать автоматически. Тогда я думаю, что на самом деле вопрос в том, как сделать это как можно проще. В идеале, решение не требует, чтобы вы знали такие вещи, как «сколько у меня ядер». Еще одно свойство, которое вам может потребоваться, - это возможность выполнять списки в одной читаемой строке.

Некоторые из приведенных ответов уже имеют хорошие свойства, подобные этому, но другой альтернативой является Ray ( docs ), который является основой для написания параллельного Python. В Ray вы бы сделали это так:

import ray

# Start Ray. This creates some processes that can do work in parallel.
ray.init()

# Add this line to signify that the function can be run in parallel (as a
# "task"). Ray will load-balance different `square` tasks automatically.
@ray.remote
def square(x):
    return x * x

# Create some parallel work using a list comprehension, then block until the
# results are ready with `ray.get`.
ray.get([square.remote(x) for x in range(1000)])
1 голос
/ 08 марта 2011

Не в пределах понимания списка AFAIK.

Конечно, вы можете сделать это с помощью традиционного цикла for и модулей многопроцессорной обработки / многопоточности.

1 голос
/ 08 марта 2011

Здесь представлен полный список параллельных пакетов для Python:

http://wiki.python.org/moin/ParallelProcessing

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

Редактировать: Может также представлять интерес следующее:

http://www.mblondel.org/journal/2009/11/27/easy-parallelization-with-data-decomposition/

...