#python #multiprocessing #python-multiprocessing
#python #многопроцессорная обработка #python-многопроцессорная обработка
Вопрос:
В Python multiprocessing
модуль может использоваться для параллельного запуска функции над диапазоном значений. Например, это создает список из первых 100000 оценок f.
def f(i):
return i * i
def main():
import multiprocessing
pool = multiprocessing.Pool(2)
ans = pool.map(f, range(100000))
return ans
Можно ли сделать аналогичную вещь, когда f принимает несколько входных данных, но изменяется только одна переменная? Например, как бы вы распараллелили это:
def f(i, n):
return i * i 2*n
def main():
ans = []
for i in range(100000):
ans.append(f(i, 20))
return ans
Ответ №1:
Вы можете использовать functools.partial()
def f(i, n):
return i * i 2*n
def main():
import multiprocessing
pool = multiprocessing.Pool(2)
ans = pool.map(functools.partial(f, n=20), range(100000))
return ans
Комментарии:
1. Я знаю, что это разрешено, но почему, учитывая, что могут быть изменены только функции, определенные на верхнем уровне модуля?
2. Можете ли вы прояснить момент об использовании partial — похоже, что он игнорирует ключи для аргумента: если я хочу использовать pool.map для ВТОРОГО аргумента —
partial(f, i=20)
— Я получил ошибку: получил несколько значений для аргументаi
.3. @Mikhail_Sam docs.python.org/2/library/functools.html#functools.partial Функция, которую вы добавляете к частичному, должна иметь первый аргумент в качестве позиционного аргумента (например, ‘i’ при запуске цикла for), а остальные аргументы ключевого слова должны быть после этого. Все значения ‘i’ добавляются в виде списка / диапазона в качестве второго аргумента функции ‘pool.map’. В вашем примере вы указали значение ‘i’ в частичной функции, когда значения для ‘i’ уже доступны в качестве второго аргумента функции ‘pool’, что приводит к самоочевидной ошибке/
Ответ №2:
Есть несколько способов сделать это. В примере, приведенном в вопросе, вы могли бы просто определить функцию-оболочку
def g(i):
return f(i, 20)
и передайте эту оболочку map()
. Более общий подход заключается в том, чтобы иметь оболочку, которая принимает один аргумент кортежа и распаковывает кортеж в несколько аргументов
def g(tup):
return f(*tup)
или используйте эквивалентное лямбда-выражение: lambda tup: f(*tup)
.
Ответ №3:
Если вы используете мой fork of multiprocessing
, called pathos
, вы можете получить пулы, которые принимают несколько аргументов… а также принимать lambda
функции. Самое приятное в этом то, что вам не нужно изменять свои программные конструкции, чтобы они соответствовали параллельной работе.
>>> def f(i, n):
... return i * i 2*n
...
>>> from itertools import repeat
>>> N = 10000
>>>
>>> from pathos.pools import ProcessPool as Pool
>>> pool = Pool()
>>>
>>> ans = pool.map(f, xrange(1000), repeat(20))
>>> ans[:10]
[40, 41, 44, 49, 56, 65, 76, 89, 104, 121]
>>>
>>> # this also works
>>> ans = pool.map(lambda x: f(x, 20), xrange(1000))
>>> ans[:10]
[40, 41, 44, 49, 56, 65, 76, 89, 104, 121]
Комментарии:
1. Только что установленный pathos — гораздо приятнее использовать локальные функции с замыканиями и т.д. без каких-либо глобальных частичных функций или функций-оболочек или чего-либо еще. Спасибо за это.
2. @AlexL: обратите внимание, что если вам нужен точно такой же интерфейс, что и
multiprocessing
, но с лучшей сериализацией, вы можете использовать альтернативноmultiprocess
(pathos
устанавливает его как зависимость).
Ответ №4:
Этот метод известен как каррирование:https://en.wikipedia.org/wiki/Currying
Другой способ сделать это без использования, functools.partial
используя классическую map
команду внутри pool.map
:
def f(args):
x, fixed = args
# FUNCTIONALITY HERE
pool = multiprocessing.Pool(multiprocessing.cpu_count() - 1)
pool.map(f, map(lambda x: (x, fixed), arguments))
Ответ №5:
Вы можете использовать каррирование бедняка (иначе обернуть его):
new_f = lambda x: f(x, 20)
затем вызовите new_f(i)
.
Комментарии:
1. Thils не будет работать с картой многопроцессорной обработки, потому что она не поддерживает функции, которые не являются «импортируемыми» (с использованием инструмента pickle)