繁体   English   中英

numpy.dot 的效率与动态与 static arrays

[英]Efficiency of numpy.dot with dynamic vs. static arrays

我必须在我的数据处理管道中做很多积。 因此,我正在试验以下两段代码,其中一段代码的效率是最慢的代码的 3 倍(就运行时间而言)。

最慢的方法(使用动态创建的 arrays)

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)

最快的方法(使用 static 阵列)

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)

为什么动态生成arrays 的第一种方法比第二种方法慢 3 倍? 是因为在第一种方法中,这些额外的大部分时间都花在为元素分配 memory 上吗? 还是其他一些导致这种退化的因素?


为了获得更多的理解,我还在纯 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 function 实际上并没有创建所有这些元素。 它进行惰性评估(即它是动态生成的)。

注意:纯 Python impl。 只是为了让自己相信数组分配可能是造成拖累的因素。 这并不意味着将其与 NumPy 实现进行比较。

速度差异是由于在较慢的情况下分配 arrays 所致。 我贴的是%timeit的output,这两种情况都考虑了arrays的分配。 OP 的 timeit 命令仅考虑了较慢情况下的分配,但不考虑较快情况下的分配。

%timeit np.dot(np.arange(200000), np.arange(200000, 400000)) 
# 524 µs ± 11.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit vec1_arr = np.arange(200000);  vec2_arr = np.arange(200000, 400000); np.dot(vec1_arr, vec2_arr) 
# 523 µs ± 17.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

arrays 的分配在我的机器上大约需要 360 微秒,而np.dot操作需要 169 微秒。 这两个持续时间之和为 529 微秒,相当于上面的%timeit output 的 output。

%timeit vec1_arr = np.arange(200000);  vec2_arr = np.arange(200000, 400000)
# 360 µs ± 16.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

vec1_arr = np.arange(200000) 
vec2_arr = np.arange(200000, 400000) 

%timeit np.dot(vec1_arr, vec2_arr) 
# 169 µs ± 5.24 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

纯 Python 测试是不公平的。 因为np.arange(200000)真的返回一个数组,而range(200000)只返回一个生成器。 所以这两种方法都可以即时创建 arrays。

import operator

%timeit sum(map(operator.mul, range(200000), range(200000, 400000)))
# 15.1 ms ± 45.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

vec1 = range(200000)
vec2 = range(200000, 400000)
%timeit sum(map(operator.mul, vec1, vec2)) 
# 15.2 ms ± 117 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

vec1 = list(range(200000))
vec2 = list(range(200000, 400000))

%timeit sum(map(operator.mul, vec1, vec2)) 
# 12.4 ms ± 716 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

我们可以看到分配的时间成本:

import numpy as np

%timeit np.arange(200000), np.arange(200000, 400000)
# 632 µs ± 9.45 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit np.dot(np.arange(200000), np.arange(200000, 400000)) 
# 703 µs ± 5.27 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

vec1_arr = np.arange(200000) 
vec2_arr = np.arange(200000, 400000) 

%timeit np.dot(vec1_arr, vec2_arr) 
# 77.7 µs ± 427 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

说得通。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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