使用带关键字参数的 multiprocessing.Pool.map() 函数?
Using the multiprocessing.Pool.map() function with keyword arguments?
我正在尝试将关键字参数传递给 Python 的 multiprocessing.Pool
实例中的 map
函数。
从 Using map() function with keyword arguments 推断,我知道我可以使用 functools.partial()
,如下所示:
from multiprocessing import Pool
from functools import partial
import sys
# Function to multiprocess
def func(a, b, c, d):
print(a * (b + 2 * c - d))
sys.stdout.flush()
if __name__ == '__main__':
p = Pool(2)
# Now, I try to call func(a, b, c, d) for 10 different a values,
# but the same b, c, d values passed in as keyword arguments
a_iter = range(10)
kwargs = {'b': 1, 'c': 2, 'd': 3}
mapfunc = partial(func, **kwargs)
p.map(mapfunc, a_iter)
输出正确:
0
2
4
6
8
10
12
14
16
18
这是这样做的最佳做法(大多数 "pythonic" 方式)吗?我觉得:
1)Pool
常用;
2) 常用关键字参数;
3) 但是像我上面的例子这样的组合用法有点像"hacky"实现这个的方法。
如果默认参数很大,使用 partial
可能不是最佳选择。传递给 map
的函数在发送给 worker 时被重复 pickle
-ed(对于可迭代对象中的每个参数一次);全局 Python 函数(本质上)是通过发送限定名称来 pickle
编辑的(因为在另一端定义了相同的函数而无需传输任何数据),而 partial
是 pickle
-ed 作为函数的 pickle
和所有提供的参数。
如果 kwargs
都是小基元,就像你的例子一样,这并不重要;发送额外参数的增量成本是微不足道的。但是,如果 kwargs
很大,比如 kwargs = {'b': [1] * 10000, 'c': [2] * 20000, 'd': [3]*30000}
,那将是一个令人讨厌的代价。
在那种情况下,您有一些选择:
在全局级别滚动您自己的函数,其工作方式类似于 partial
,但 pickle
不同:
class func_a_only(a):
return func(a, 1, 2, 3)
将 initializer
参数用于 Pool
,这样每个工作进程都会设置一次状态,而不是每个任务一次,这样您就可以确保数据可用,即使您重新在基于 spawn 的环境中工作(例如 Windows)
使用Manager
s在所有进程之间共享一个数据副本
可能还有一些其他方法。重点是,partial
对于不会产生巨大 pickle
s 的参数来说很好,但如果绑定参数很大,它可能会杀了你。
注意:在这种特殊情况下,如果您使用的是 Python 3.3+,您实际上 不需要 partial
,并且避免dict
支持 tuple
s 节省了微不足道的开销。在不添加任何新功能的情况下,仅添加一些导入,您可以替换:
kwargs = {'b': 1, 'c': 2, 'd': 3}
mapfunc = partial(func, **kwargs)
p.map(mapfunc, a_iter)
与:
from itertools import repeat
p.starmap(func, zip(a_iter, repeat(1), repeat(2), repeat(3)))
达到类似的效果。需要明确的是,partial
和 "fixes" 没有任何问题(两种方法在处理大型对象时都会遇到同样的问题),这只是偶尔有用的替代方法。
我正在尝试将关键字参数传递给 Python 的 multiprocessing.Pool
实例中的 map
函数。
从 Using map() function with keyword arguments 推断,我知道我可以使用 functools.partial()
,如下所示:
from multiprocessing import Pool
from functools import partial
import sys
# Function to multiprocess
def func(a, b, c, d):
print(a * (b + 2 * c - d))
sys.stdout.flush()
if __name__ == '__main__':
p = Pool(2)
# Now, I try to call func(a, b, c, d) for 10 different a values,
# but the same b, c, d values passed in as keyword arguments
a_iter = range(10)
kwargs = {'b': 1, 'c': 2, 'd': 3}
mapfunc = partial(func, **kwargs)
p.map(mapfunc, a_iter)
输出正确:
0
2
4
6
8
10
12
14
16
18
这是这样做的最佳做法(大多数 "pythonic" 方式)吗?我觉得:
1)Pool
常用;
2) 常用关键字参数;
3) 但是像我上面的例子这样的组合用法有点像"hacky"实现这个的方法。
如果默认参数很大,使用 partial
可能不是最佳选择。传递给 map
的函数在发送给 worker 时被重复 pickle
-ed(对于可迭代对象中的每个参数一次);全局 Python 函数(本质上)是通过发送限定名称来 pickle
编辑的(因为在另一端定义了相同的函数而无需传输任何数据),而 partial
是 pickle
-ed 作为函数的 pickle
和所有提供的参数。
如果 kwargs
都是小基元,就像你的例子一样,这并不重要;发送额外参数的增量成本是微不足道的。但是,如果 kwargs
很大,比如 kwargs = {'b': [1] * 10000, 'c': [2] * 20000, 'd': [3]*30000}
,那将是一个令人讨厌的代价。
在那种情况下,您有一些选择:
在全局级别滚动您自己的函数,其工作方式类似于
partial
,但pickle
不同:class func_a_only(a): return func(a, 1, 2, 3)
将
initializer
参数用于Pool
,这样每个工作进程都会设置一次状态,而不是每个任务一次,这样您就可以确保数据可用,即使您重新在基于 spawn 的环境中工作(例如 Windows)使用
Manager
s在所有进程之间共享一个数据副本
可能还有一些其他方法。重点是,partial
对于不会产生巨大 pickle
s 的参数来说很好,但如果绑定参数很大,它可能会杀了你。
注意:在这种特殊情况下,如果您使用的是 Python 3.3+,您实际上 不需要 partial
,并且避免dict
支持 tuple
s 节省了微不足道的开销。在不添加任何新功能的情况下,仅添加一些导入,您可以替换:
kwargs = {'b': 1, 'c': 2, 'd': 3}
mapfunc = partial(func, **kwargs)
p.map(mapfunc, a_iter)
与:
from itertools import repeat
p.starmap(func, zip(a_iter, repeat(1), repeat(2), repeat(3)))
达到类似的效果。需要明确的是,partial
和 "fixes" 没有任何问题(两种方法在处理大型对象时都会遇到同样的问题),这只是偶尔有用的替代方法。