我知道在Python中,就地运算符将__iadd__
方法用于就地运算符。对于不可变类型,__iadd__
是使用__add__
的一种解决方法,例如,像tmp = a + b; a = tmp
一样,但是可变类型(例如列表)被就地修改,这会导致速度略有提高。
但是,如果我有一个NumPy数组,可以在其中修改其包含的不可变类型(例如整数或浮点数),那么速度也会大大提高。这是如何运作的?我在下面做了一些示例基准测试:
import numpy as np
def inplace(a, b):
a += b
return a
def assignment(a, b):
a = a + b
return a
int1 = 1
int2 = 1
list1 = [1]
list2 = [1]
npary1 = np.ones((1000,1000))
npary2 = np.ones((1000,1000))
print('Python integers')
%timeit inplace(int1, 1)
%timeit assignment(int2, 1)
print('\nPython lists')
%timeit inplace(list1, [1])
%timeit assignment(list2, [1])
print('\nNumPy Arrays')
%timeit inplace(npary1, 1)
%timeit assignment(npary2, 1)
当我在NumPy数组上使用就地运算符时,我期望的是与Python整数相似的区别,但是结果完全不同:
Python integers
1000000 loops, best of 3: 265 ns per loop
1000000 loops, best of 3: 249 ns per loop
Python lists
1000000 loops, best of 3: 449 ns per loop
1000000 loops, best of 3: 638 ns per loop
NumPy Arrays
100 loops, best of 3: 3.76 ms per loop
100 loops, best of 3: 6.6 ms per loop
最佳答案
每次调用assignment(npary2, 1)
都需要创建一个新的一百万个元素数组。考虑一下分配一个(1000,1000)形的数组需要花费多少时间:
In [21]: %timeit np.ones((1000, 1000))
100 loops, best of 3: 3.84 ms per loop
在我的计算机上,这种新的临时数组的分配大约需要3.84毫秒,并且在正确的数量级上可以解释
inplace(npary1, 1)
和assignment(nparay2, 1)
之间的全部差异:In [12]: %timeit inplace(npary1, 1)
1000 loops, best of 3: 1.8 ms per loop
In [13]: %timeit assignment(npary2, 1)
100 loops, best of 3: 4.04 ms per loop
因此,鉴于分配是一个相对较慢的过程,因此就地添加明显比分配给新阵列快得多。
NumPy数组上的NumPy操作可能很快,但是NumPy数组的创建相对较慢。例如,考虑创建NumPy数组比使用Python列表花费更多的时间:
In [14]: %timeit list()
10000000 loops, best of 3: 106 ns per loop
In [15]: %timeit np.array([])
1000000 loops, best of 3: 563 ns per loop
这就是为什么通常最好使用一个大型NumPy数组(分配一次)而不是使用数千个小的NumPy数组的原因。
关于python - NumPy的现场运算符(operator)如何实现以解释显着的性能提升,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24354096/