在python / numpy中优化矩阵写入

时间:2013-12-26 00:23:15

标签: python performance optimization numpy matrix

我目前正在尝试优化一段代码,我们要经历并计算一堆值并将其写入矩阵。计算的顺序并不重要:

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循环的显着加速。 (我通过状态变量传递了附加数据):

3 个答案:

答案 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()调用更快,但它可以使后续计算可以进行矢量化。