![](/img/trans.png)
[英]How to correctly pass the generator to the function parameters of ThreadPoolExecutor Executor.map()?
[英]How to extract result from a generator object that created by executor.map()
我正在尝试测试concurrent.futures
以加快计算速度。 这是测试代码:
import concurrent.futures
import numpy as np
import random
def task(n):
c = np.sum(n)
# print sum result
print(c)
return c
# generate a 3D array
b = np.random.rand(2, 3, 100)
executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)
# along axis=2 to do sum in task function
v = executor.map(np.apply_along_axis(task, 2, b))
list(v)
我可以看到executor.map()
进行计算,因为task
function 中的print(c)
可以打印sum
结果。
问题是:
v
获取数据,这是一个生成器 object?list(v)
时,它会给出[]
作为结果。 为什么?主要问题是误解。 np.apply_along_axis
将重复(但串行)调用 function task
,然后返回修改后的数组。 然后将返回的数组传递给executor.map
,此时它实际上期望找到 function。
因此,调用您的 function 的不是executor.map
,而是apply_along_axis
。
正如我上面提到的,如果您的任务受 CPU 限制,Python 线程将无助于节省时间。 或许你应该问一个不同的问题,包括你的情况的更多细节,而不是像这样发明一个假的问题。
首先了解apply
做什么,不做什么
将 function 修改为更具诊断性:
def task(n):
print(n)
c = np.sum(n)
# print sum result
print(c)
return c
并使用较小的阵列:
In [205]: b = np.arange(24).reshape(2, 3, 4)
In [207]: np.apply_along_axis(task, 2, b)
[0 1 2 3]
6
[4 5 6 7]
22
[ 8 9 10 11]
38
[12 13 14 15]
54
[16 17 18 19]
70
[20 21 22 23]
86
Out[207]:
array([[ 6, 22, 38],
[54, 70, 86]])
apply
将b
的连续“行”传递给task
。 结果是 (2,3) 数组 - b
数组沿其最后一个轴缩小。
如果task
真的这么简单,您可以使用轴参数更快地获得相同的结果。
In [208]: np.sum(b, axis=2)
Out[208]:
array([[ 6, 22, 38],
[54, 70, 86]])
在花费大量精力使线程/多处理工作之前,请尝试充分利用numpy
编译代码。
apply
本质上是:
In [209]: np.array([[task(row) for row in plane] for plane in b])
[0 1 2 3]
6
[4 5 6 7]
22
...
Out[209]:
array([[ 6, 22, 38],
[54, 70, 86]])
修改task
以省略打印,我们可以比较时间:
In [211]: timeit np.apply_along_axis(task1, 2, b)
154 µs ± 6.86 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [212]: timeit np.array([[task1(row) for row in plane] for plane in b])
72 µs ± 53.6 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [213]: timeit np.sum(b, axis=2)
10.9 µs ± 18.5 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
===
来自executor.map
文档
map(func, *iterables, timeout=None, chunksize=1)
第一个 arg 是func
,一个 function,不是调用 function 的结果。第二个是一个或多个可迭代对象。
也许可以使用
map(task, b.reshape(-1,100))
并取回“行”总和列表。
In [217]: v = executor.map(task, b.reshape(-1, 4))
[0 1 2 3]
6
[4 5 6 7]
22
[ 8 9 10 11]
38
...
In [218]: v
Out[218]: <generator object Executor.map.<locals>.result_iterator at 0x7f235c85be40>
In [219]: list(v)
Out[219]: [6, 22, 38, 54, 70, 86]
对于这个小例子和我的小机器,多处理根本没有帮助:
In [220]: timeit list(executor.map(task1, b.reshape(-1, 4)))
308 µs ± 35.9 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.