我有一个函数,它接受一个数组,一个位置和一个数字k。该函数创建从零到k的所有数字,然后使用这些数字替换位置pos中输入数组的k个副本的元素。
香草循环版本是这样的:
def expand1(array,pos,k):
res = []
states = range(0,k)
for state in states:
aux = array.copy()
aux[pos] = state
res.append(aux)
return(res)
大约需要 770 ns±6.35 ns
我正在尝试使用Numpy对此进行优化。我的函数重写
def expand2(array,pos,k):
aux = np.tile(array,(k,1))
aux[:,pos] = np.arange(k)
return aux
,这需要 6.26 µs±65.7 ns ,这要长得多。
我想知道人们在优化python代码时可能拥有的经验,例如克隆数组几次,并对每个副本执行不同的操作。
对任何可能缺乏礼节的行为表示歉意。这是我的第一篇文章。
谢谢。
答案 0 :(得分:1)
经常使用numpy时,您需要付出一定的固定开销才能获得单件加速效果。因此,在对numpy代码段进行基准测试时,重要的是要考虑不同的问题大小。
让我们使用您的两个函数和我编写的第三个函数来做到这一点:
x轴为k,y轴为时间
首先关注一下expand1和expand 2。循环的每件商品成本(坡度)较高,但由于开销(y截距)恒定而使np.tile
的胜率小k。
转到expand3,我们可以进一步看到,通过避免使用便利功能(例如np.tile
),可以显着减少开销。
代码:
import numpy as np
def expand1():
res = []
states = range(0,k)
for state in states:
aux = array.copy()
aux[pos] = state
res.append(aux)
return res
def expand2():
aux = np.tile(array,(k,1))
aux[:,pos] = np.arange(k)
return aux
def expand3():
aux = np.empty((k, *array.shape), array.dtype)
aux[...] = array
aux[:, pos] = np.arange(k)
return aux
array = np.random.randint(0, 100, 1000)
pos = 493
from timeit import repeat
T = []
for k in range(100):
T.append([min(repeat(f, number=100)) for f in (expand1, expand2, expand3)])
import pylab
pylab.plot(T)
pylab.legend('expand1 expand2 expand3'.split())
pylab.show()