2015-10-24 4 views
1

Ломая от А map работ, как и ожидалось:Многопроцессорность Python - Как эффективно отрываться от карты?

def worker(x): 
    print("worker call x=%s" % x) 
    return x 

for x in map(worker, range(5)): 
    print(x) 
    if x == 2: 
     break 

worker call x=0 
0 
worker call x=1 
1 
worker call x=2 
2 

Но если бы я сделать то же самое с multiprocessing, я получаю это: карта

from multiprocessing import Pool 

pool = Pool(2) 
for x in pool.map(worker, range(5)): 
    print(x) 
    if x == 2: 
     break 
pool.close() 
pool.join() 

0 
1 
2 
worker call x=0 
worker call x=1 
worker call x=2 
worker call x=3 
worker call x=4 

Почему многопроцессорных-х ведет себя по-другому? Как избежать ненужных вызовов функций?

ответ

2

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

Если вы не знакомы с параллельными принципами, я попытаюсь кратко объяснить это немного лучше.

В вашем первом примере, используя встроенную карту, код создаст итерируемый объект, который позволит вам выполнить worker по одному, последовательно. Тот факт, что он выполняет один за раз и последовательно, означает, что ваша функция, которая печатает worker call x=, всегда будет печататься первой, прежде чем выполнение продолжится во внутренней части вашего цикла, которая будет печатать только значение x. Это также означает, что когда ваша петля достигает 2, вы можете выйти из цикла без каких-либо дополнительных вызовов на карту или на тело цикла. Это синхронная операция, все вежливо и ждет своей очереди.

В вашем втором примере использование кода мультипроцессорной карты все равно создаст итерируемый объект, который обрабатывает worker(x). На этот раз, однако, вы не выполняете каждый звонок до worker(x) по одному (синхронно). Многопроцессорный вызов карты немедленно отправит все вызовы карты отдельным процессам для выполнения сначала, а затем объедините результаты. Затем ваш цикл выполняет комбинированные результаты и снова останавливается на 2, как вы его инструктировали. К сожалению, все записи в карте уже выполнялись в отдельных процессах, так что тело цикла выполнялось минимальное количество раз, а карта не была.

Надеюсь, это поможет вам понять, почему немного лучше.

1

Основополагающий характер multiprocessing.Pool состоит в том, что, как только вы говорите pool.map(...), он отправляет все задачи в переданном итерируемом в очередь для рабочих процессов. Как только такая задача будет помещена в пул, она в конечном итоге будет потребляться рабочим процессом и обработана. Ничто из этого не может изменить.

1

Следует отметить, что если вы испробовали первую версию с Python2.x (я), результат был бы:

worker call x=0 
worker call x=1 
worker call x=2 
worker call x=3 
worker call x=4 
0 
1 
2 

без многопроцессорной участие.

Разница заключается в том, что в Python 2 doc состояний:

Применить функцию к каждому элементу из итератора и возвращает список результатов ...

когда Python 3 doc состояние:

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

Это означает, что map был изменен в Python 3, чтобы возвращать итерируемый вместо списка.

И даже в Python 3, multiprocessing.pool.Pool.map док говорит:

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

(подчеркнуть мой)

Это означает, что метод сначала вычисляет список результатов порождая несколько процессов, и только затем возвращает полный результирующий объект, а не получая значение от каждый раз, когда заканчивается подпроцесс. Таким образом, он ближе к Python2 map, чем к Python3.

Смежные вопросы