Concurrent.futures vs Multiprocessing в Python 3

Python 3.2 представил Concurrent Futures, которые, как представляется, представляют собой некоторую передовую комбинацию старой потоковой передачи и multiprocessing.

В чем преимущества и недостатки использования этого для задач с привязкой к процессору над старым многопроцессорным модулем?

Эта статья предполагает, что с ними гораздо легче работать - это тот случай?

Ответ 1

Я бы не назвал concurrent.futures более "расширенным" - это более простой интерфейс, который работает очень точно, независимо от того, используете ли вы несколько потоков или несколько процессов в качестве основного трюка распараллеливания.

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

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

Изменить: пример

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

from concurrent.futures import ProcessPoolExecutor
def pool_factorizer_map(nums, nprocs):
    # Let the executor divide the work among processes by using 'map'.
    with ProcessPoolExecutor(max_workers=nprocs) as executor:
        return {num:factors for num, factors in
                                zip(nums,
                                    executor.map(factorize_naive, nums))}

Здесь точно то же самое, что и с помощью multiprocessing:

import multiprocessing as mp
def mp_factorizer_map(nums, nprocs):
    with mp.Pool(nprocs) as pool:
        return {num:factors for num, factors in
                                zip(nums,
                                    pool.map(factorize_naive, nums))}

Обратите внимание, что возможность использования объектов multiprocessing.Pool в качестве контекстных менеджеров была добавлена ​​в Python 3.3.

С какой из них легче работать? LOL;-) Они практически идентичны.

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

Опять же, все эти разные способы - это сила и слабость. Это сила, потому что в некоторых ситуациях гибкость может потребоваться. Это слабость из-за "предпочтительно только одного очевидного способа сделать это". По-видимому, проект, который, если возможно, будет concurrent.futures, будет легче поддерживать в долгосрочной перспективе из-за отсутствия безвозмездной новизны в том, как можно использовать его минималистический API.