Подтвердить что ты не робот

Как распараллеливать вычисления в Python?

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

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

каждый x * x-Расчет мог (по крайней мере теоретически) выполняться параллельно.

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

4b9b3361

Ответ 1

Как сказал Кен, это невозможно, но с мультипроцессорным модулем 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)))

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

Ответ 2

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

IMHO эффективная автоматическая парализация понимания списка невозможна без дополнительной информации (например, предоставляемой с использованием директив в 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

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

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

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

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

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

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

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

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

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

Если требование заключается в том, чтобы как можно ближе придерживаться списка, то jug кажется ближайшим совпадением. Из tutorial распределение задач по нескольким экземплярам может быть таким же простым, как:

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, файловая система, в памяти), что означает, что процессы могут охватывать узлы кластера.

Ответ 3

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

Он также доступен в качестве 2.6+ backport.

Ответ 4

Для параллелизма с общей памятью я рекомендую 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))

Ответ 5

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

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

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

Ответ 6

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

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

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

Изменить: Также может быть интересно следующее:

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

Ответ 7

Не в понимании списка AFAIK.

Вы могли бы сделать это с помощью традиционного цикла цикла и многопроцессорных/потоковых модулей.

Ответ 8

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

Некоторые из приведенных ответов, похоже, уже обладают такими хорошими свойствами, но другой альтернативой является 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)])