繁体   English   中英

如何从 executor.map() 创建的生成器 object 中提取结果

[英]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结果。

问题是:

  1. 如何从v获取数据,这是一个生成器 object?
  2. 当我使用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]])

applyb的连续“行”传递给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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM