使用Numpy.tile优化循环:需要更长的时间吗?

时间:2018-12-19 00:19:32

标签: python numpy for-loop optimization vectorization

我有一个函数,它接受一个数组,一个位置和一个数字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代码时可能拥有的经验,例如克隆数组几次,并对每个副本执行不同的操作。

对任何可能缺乏礼节的行为表示歉意。这是我的第一篇文章。

谢谢。

1 个答案:

答案 0 :(得分:1)

经常使用numpy时,您需要付出一定的固定开销才能获得单件加速效果。因此,在对numpy代码段进行基准测试时,重要的是要考虑不同的问题大小。

让我们使用您的两个函数和我编写的第三个函数来做到这一点:

enter image description here

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()