Как я могу распараллелить цикл for в python, используя многопроцессорный пакет? - PullRequest
1 голос
/ 15 апреля 2019

Примечание : мне не нужна связь между процессами / потоками, меня интересует только сигнал завершения (вот почему я разместил этот вопрос как новый, так как все остальные примеры я 'обнаружил связь между собой).

Как использовать пакет multiprocessing в Python 3 для распараллеливания следующего фрагмента кода (конечная цель - ускорить его выполнение):

a = 123
b = 456
for id in ids: # len(ids) = 10'000
   # executes a binary with CLI flags
   run_binary_with_id(id, a, b) 
   # i.e. runs "./hello_world_exec --id id --a a --b b" which takes about 30 seconds on average

Я попробовал следующее:

import multiprocessing as mp

def run_binary_with_id(id, a, b):
    run_command('./hello_world_exec --id {} --a {} --b {}'.format(id, a, b))

if __name__ == '__main__':
    ctx = mp.get_context('spawn')
    q = ctx.Queue()
    a = 123
    b = 456
    ids = range(10000)
    for id in ids:
       p = ctx.Process(target=run_binary_with_id, args=(id,a,b))
       p.start()
    p.join()
    # The binary was executed len(ids) number of times, do other stuff assuming everything's completed at this point

или

for id in ids:
   map.apply_async(run_binary_with_id, (id,a,b))

В подобном вопросе ответ следующий:

def consume(iterator):
    deque(iterator, max_len=0)
x=pool.imap_unordered(f,((i,j) for i in range(10000) for j in range(10000)))
consume(x)

которого я совсем не понимаю (зачем мне это consume()).

1 Ответ

1 голос
/ 15 апреля 2019

Попытка порождать 10000 процессов для параллельного запуска почти наверняка приведет к перегрузке вашей системы и замедлению ее выполнения по сравнению с последовательным выполнением процессов из-за накладных расходов, связанных с тем, что ОС постоянно выполняет переключение контекста между процессами, когда число процессы намного превышают количество процессоров / ядер, которые есть в вашей системе.

Вместо этого можно использовать multiprocessing.Pool, чтобы ограничить число рабочих процессов, порождаемых для задачи. Конструктор Pool ограничивает количество процессов числом ядер, которые есть в вашей системе по умолчанию, но вы можете настроить его, если хотите, с параметром processes. Затем вы можете использовать его метод map, чтобы легко отобразить последовательность аргументов для применения к данной функции для параллельного запуска. Однако он может отображать только один аргумент функции, поэтому вам придется использовать functools.partial для предоставления значений по умолчанию для других аргументов, которые в вашем случае не изменяются между вызовами:

from functools import partial
if __name__ == '__main__':
    _run_binary_with_id = partial(run_binary_with_id, a=123, b=456)
    with mp.Pool() as pool:
        pool.map(_run_binary_with_id, range(10000))
...