Python: Как распараллелить функции, используемые во вложенных циклах for со многими входами функций? - PullRequest
0 голосов
/ 28 августа 2018

Я рассчитываю ускорить медленный цикл, но не думаю, что я иду к этому с наилучшим подходом. Я хотел бы распараллелить некоторый код, который выполняет функцию, которую я написал, и у меня возникли некоторые проблемы, пытаясь выяснить, как именно сформулировать входные параметры при использовании модуля multiprocessing в Python. Код, который у меня есть, в основном имеет следующую форму:

a = some_value
b = some_value
c = some_value
for i in range(1,101):
    for j in range(1,101):
        b = np.array([i*0.001,j*0.001]).reshape((2,1))
        (A,B,C,D) = function(a,b,c,d)

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

pool = mp.Pool(processes=4)
input_parameters = *list of iterables for multiprocessing*
result = pool.map(paramest.parameter_estimate_ND, input_parameters)

, где список итераций создается с помощью модуля itertools. Поскольку я изменяю только одну входную переменную функции, а все остальные объявляются до того, как возникнут проблемы с структурированием таких входных параметров. Так что я действительно хотел бы использовать multiprocessing для одновременного запуска различных входов, чтобы ускорить выполнение циклов for.

Тогда у меня возникает вопрос: как можно структурировать использование multiprocessing для распараллеливания кода, выполняющегося в функции, при изменении только входных данных определенных переменных?

Подхожу ли я к этому наилучшим образом? Есть ли лучший способ сделать такую ​​вещь?

Спасибо!

1 Ответ

0 голосов
/ 28 августа 2018

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


Итак, как вы распараллеливаете этот внутренний цикл?

Просто включите его в функцию:

def wrapper(a, c, d, i, j):
    b = np.array([i*0.001,j*0.001]).reshape((2,1))
    return function(a,b,c,d)

А сейчас:

for i in range(1,101):
    pfunc = partial(function, a, c, d, i)
    ABCDs = pool.map(pfunc, range(1, 101))

Или вместо создания частичного вы можете просто определить функцию-оболочку внутри цикла i:

for i in range(1,101):
    def wrapper(j):
        b = np.array([i*0.001,j*0.001]).reshape((2,1))
        return function(a,b,c,d)
    ABCDs = pool.map(wrapper, range(1, 101))

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

for i in range(1,101):
    def wrapper(j, *, a=a, c=c, d=d, i=i):
        b = np.array([i*0.001,j*0.001]).reshape((2,1))
        return function(a,b,c,d)
    ABCDs = pool.map(wrapper, range(1, 101))

Если окажется, что одного j недостаточно для параллелизма, вы можете легко заменить его на (i, j):

def wrapper(i, j, *, a=a, b=b, c=c, d=d):
    b = np.array([i*0.001,j*0.001]).reshape((2,1))
    return function(a,b,c,d)
for i in range(1,101):
    ABCDs = pool.map(wrapper, itertools.product(range(1, 101), range(1, 101)))

То, что ABCDs будет итерируемым из A, B, C, D значений, так что, скорее всего, все, что вы хотели сделать с A, B, C, D, зависит только от:

    for A, B, C, D in ABCDs:
        # whatever
...