我最近遇到了great SO post,其中用户建议在处理NumPy数组时numpy.sum
比Python的sum
更快。
这让我想到,使用NumPy函数比运算符更快地对NumPy数组进行元素操作?如果是这样,那么为什么会这样呢?
请考虑以下示例。
import numpy as np
a = np.random.random(1e10)
b = np.random.random(1e10)
np.subtract(a, b)
是否会比a - b
可靠得快?
答案 0 :(得分:14)
不,不是很重要。
np.sum
比sum
快的原因是sum
被实现为“天真地”迭代迭代(在本例中为numpy数组),调用元素'{{ 1}}运算符(这会产生很大的开销),而numpy的__add__
实现被优化,例如利用它知道元素的类型(dtype)的事实,并且它们在内存中是连续的。
sum
和np.subtract(arr1, arr2)
不是这种情况。后者粗略地转化为前者。
不同之处在于可以覆盖python中的减法运算符,因此numpy数组会覆盖它以使用优化版本。但是,arr1-arr2
操作不可覆盖,因此numpy提供了另一种优化版本。
答案 1 :(得分:8)
不是真的。你可以很容易地检查时间。
a = np.random.normal(size=1000)
b = np.random.normal(size=1000)
%timeit np.subtract(a, b)
# 1000000 loops, best of 3: 1.57 µs per loop
%timeit a - b
# 1000000 loops, best of 3: 1.47 µs per loop
%timeit np.divide(a, b)
# 100000 loops, best of 3: 3.51 µs per loop
%timeit a / b
# 100000 loops, best of 3: 3.38 µs per loop
numpy函数实际上似乎有点慢。我不确定这是否有意义,但我怀疑这可能是因为在同一实现之上的一些额外的函数调用开销。
编辑:正如@unutbu所说,可能是因为np.add
和朋友在必要时有额外的类型检查开销来将数组转换为数组,所以像np.add([1, 2], [3, 4])
这样的东西可以工作。
答案 2 :(得分:3)
@ shx2给出了很好的答案。
我将稍微展开sum
与np.sum
:
sum
将遍历一个数组,逐个获取元素,然后将它们转换为Python对象,然后将它们作为Python对象一起添加。np.sum
将使用本机代码中的优化循环对数组求和,而不对单个值进行任何转换(正如shx2指出的那样,这关键需要数组内容的同质性和连续性)每个数组元素到Python对象的转换是目前开销的主要来源。
顺便说一句,这也解释了为什么 dumb 使用Python standard-library C array type进行数学运算。 sum(list)
比<{1}}快快 。
答案 3 :(得分:1)
a-b
转换为函数调用a.__rsub__(b)
。因此,它使用属于变量的方法(例如,如果a
是数组,则编译的numpy代码)。
In [20]: a.__rsub__??
Type: method-wrapper
String Form:<method-wrapper '__rsub__' of numpy.ndarray object at 0xad27a88>
Docstring: x.__rsub__(y) <==> y-x
np.subtract(x1, x2[, out])
的文档显示它是ufunc
。 ufunc
经常使用像__rsub__
这样的已编译操作,但可能会增加一些开销以适应ufunc
协议。
在其他一些案例中,np.foo(x, args)
会转换为x.foo(args)
。
通常,如果函数和运算符最终调用编译的numpy代码来进行实际计算,那么时序将非常相似,特别是对于大型数组。