【发布时间】:2020-09-19 07:23:55
【问题描述】:
我必须在我的数据处理管道中做很多 dot 产品。因此,我正在试验以下两段代码,其中一段的效率是最慢的代码的 3 倍(就运行时间而言)。
最慢的方法(动态创建数组)
In [33]: %timeit np.dot(np.arange(200000), np.arange(200000, 400000))
352 µs ± 958 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
最快的方法(使用静态数组)
In [34]: vec1_arr = np.arange(200000)
In [35]: vec2_arr = np.arange(200000, 400000)
In [36]: %timeit np.dot(vec1_arr, vec2_arr)
121 µs ± 90.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
为什么第一种动态生成数组的方法比第二种方法慢 3 倍?是因为在第一种方法中,这些额外的大部分时间都花在为元素分配内存上吗?还是其他一些导致这种退化的因素?
为了获得更多理解,我还在纯 Python 中复制了设置。令人惊讶的是,以一种或另一种方式执行它并没有性能差异,尽管它比 numpy 实现慢,这是显而易见的和预期的。
In [42]: %timeit sum(map(operator.mul, range(200000), range(200000, 400000)))
12.5 ms ± 71.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [38]: vec1 = range(200000)
In [39]: vec2 = range(200000, 400000)
In [40]: %timeit sum(map(operator.mul, vec1, vec2))
12.5 ms ± 27.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
纯 Python 情况下的行为很清楚,因为 range 函数实际上并没有创建所有这些元素。它进行惰性评估(即它是动态生成的)。
注意:纯 Python 实现。只是为了让自己相信数组分配可能是造成拖累的因素。这并不意味着将其与 NumPy 实现进行比较。
【问题讨论】:
-
关于你的第一个问题,是的,这可能是由于分配。尝试仅对分配进行计时。所有子步骤的总和应为 ~350 µs。为了进一步确认,从我不久前做的一些测试来看,即使是广播数组(不消耗额外内存)似乎也需要相同的时间来进行元素操作(例如
+)。也许点产品的情况有所不同?我对此表示怀疑。 -
第一个包括创建数组所需的时间,大
arange。动态与静态在 numpy 中不是有效的区别。dot得到相同的数组。
标签: python performance numpy numpy-ndarray