我知道在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/

10-11 22:03
查看更多