我目前正在尝试优化一段代码,我们要经历并计算一堆值并将其写入矩阵。计算的顺序并不重要:
mat = np.zeros((n, n))
mat.fill(MAX_VAL)
for i in xrange(0, smallerDim):
for j in xrange(0,n):
similarityVal = doACalculation(i,j, data, cache)
mat[i][j] = abs(1.0 / (similarityVal + 1.0))
我已经对此代码进行了分析,并发现大约90%的时间用于将值写回矩阵(最后一行)
我想知道进行此类计算以优化写入的最佳方法是什么。我应该写一个中间缓冲区并复制整行等等。我对性能调整或numpy内部构件有点无能为力。
编辑: doACalculation不是副作用自由函数。它接收一些数据(假设这是一些python对象)以及它写入和读取一些中间步骤的缓存。我不确定它是否可以很容易地进行矢量化。我尝试使用numpy.vectorize作为推荐,但没有看到天真for循环的显着加速。 (我通过状态变量传递了附加数据):
答案 0 :(得分:4)
将它包装在numba autojit中应该可以提高性能。
def doACalculationVector(n, smallerDim):
return np.ones((smallerDim, n)) + 1
def testVector():
n = 1000
smallerDim = 800
mat = np.zeros((n, n))
mat.fill(10)
mat[:smallerDim] = abs(1.0 / (doACalculationVector(n, smallerDim) + 1.0))
return mat
@numba.autojit
def doACalculationNumba(i,j):
return 2
@numba.autojit
def testNumba():
n = 1000
smallerDim = 800
mat = np.zeros((n, n))
mat.fill(10)
for i in xrange(0, smallerDim):
for j in xrange(0, n):
mat[i,j] = abs(1.0 / (doACalculationNumba(i, j) + 1.0))
return mat
原始时间供参考:( mat[i][j]
已更改为mat[i,j]
)
In [24]: %timeit test()
1 loops, best of 3: 226 ms per loop
现在我简化了一下这个功能,因为这就是所提供的一切。但是,当测试时,testNumba的测试速度是测试的40倍。并且的速度是矢量化的3倍
In [20]: %timeit testVector()
100 loops, best of 3: 17.9 ms per loop
In [21]: %timeit testNumba()
100 loops, best of 3: 5.91 ms per loop
答案 1 :(得分:2)
如果你可以对doACalculation
进行矢量化,那么任务就变得简单了:
similarityArray = doACalculation(np.indices((smallerDim, n)))
mat[:smallerDim] = np.abs(1.0 / (similarityArray + 1))
假设您正确地向量化doACalculation
,这应该至少快一个数量级。通常,在使用NumPy数组时,您希望尽可能避免显式循环和元素访问。
供参考,可能的doACalculation
:
# Unvectorized
def doACalculation(i, j):
return i**2 + i*j + j
# Vectorized
def doACalculation(input):
i, j = input
return i**2 + i*j + j
# Vectorized, but with the original call signature
def doACalculation(i, j):
return i**2 + i*j + j
是的,最后一个版本应该与非向量化函数相同。有时这很容易。
答案 2 :(得分:1)
即使您无法向量化doACalculation()
。您可以使用numpy.vectorize()
加快计算速度。这是测试。
import numpy as np
n = 1000
smallerDim = 500
def doACalculation(i, j):
return i+j
For loop version:
%%timeit
mat = np.zeros((n, n))
for i in xrange(0, smallerDim):
for j in xrange(0,n):
similarityVal = doACalculation(i,j)
mat[i,j] = abs(1.0 / (similarityVal + 1.0))
输出:
1 loops, best of 3: 183 ms per loop
vectorize()
版本:
%%timeit
mat2 = np.zeros((n, n))
i, j = np.ix_(np.arange(smallerDim), np.arange(n))
f = np.vectorize(doACalculation, "d")
mat2[:smallerDim] = np.abs(1.0/(f(i, j) + 1))
输出:
10 loops, best of 3: 97.3 ms per loop
测试结果:
np.allclose(mat,mat2)
outpout:
True
此方法不会使doACalculation()
调用更快,但它可以使后续计算可以进行矢量化。