繁体   English   中英

优化python / numpy中的矩阵写入

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM