[英]Optimizing matrix writes in python/numpy
我目前正在尝试优化一段代码,其依据是我们计算并计算一堆值并将它们写入矩阵。 计算顺序无关紧要:
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循环有明显的提速。 (我通过状态变量传入了其他数据):
将其包装在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
如果可以向量化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
是的,最后一个版本实际上应该与未向量化的功能相同。 有时候就是那么容易。
即使您无法向量化doACalculation()
。 您可以使用numpy.vectorize()
加快计算速度。 这是测试。
import numpy as np
n = 1000
smallerDim = 500
def doACalculation(i, j):
return i+j
对于循环版本:
%%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)
输出:
True
此方法不会使doACalculation()
调用速度更快,但可以使后续计算可以向量化。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.