密集矩阵乘法优化在python中影响很小

时间:2014-12-13 23:40:41

标签: python optimization matrix

我想知道为什么i和j上的循环优化反转(它应该提供更好的数据局部性)不适用于python中的以下代码:

import random
import time

def fillRand1D(size):
    return [random.uniform(-2.0, 2.0) for _ in range(size * size)]

def mmNaive1D(A, B, size):
    C = [0] * size * size
    for i in range(size):
        for j in range(size):
            for k in range(size):
                C[i * size + j] += A[i * size + k] * B[k * size + j]
    return C

def mmInvariant1D(A, B, size):
    C = [0] * size * size
    for i in range(size):
        for j in range(size):
            sigma = C[i * size + j]
            for k in range(size):
                sigma += A[i * size + k] * B[k * size + j]
            C[i * size + j] = sigma
    return C


def mmLoop1D(A, B, size):
    C = [0] * size * size
    for k in range(size):
        for i in range(size):
            for j in range(size):
                C[i * size + j] += A[i * size + k] * B[k * size + j]
    return C


def mmLoopInvariant1D(A, B, size):
    C = [0] * size * size
    for k in range(size):
        for i in range(size):
            Aik = A[i * size + k]
            for j in range(size):
                C[i * size + j] += Aik * B[k * size + j]
    return C

def main():
    matmul_func_1D = [mmNaive1D,mmInvariant1D,mmLoop1D,mmLoopInvariant1D]
    size = 200
    A_1D = fillRand1D(size)
    B_1D = fillRand1D(size)

    for f in matmul_func_1D:
        A = A_1D[:] # copy !
        B = B_1D[:]
        start_time = time.time()
        C = f(A_1D,B_1D,size)
        # print(T)
        print(f.__name__ + " in " + str(time.time() - start_time) + " s")

if __name__ == '__main__':
    main()

结果是python:

mmNaive1D in 3.420367956161499 s  
mmInvariant1D in 2.316128730773926 s  
mmLoop1D in 3.4071271419525146 s  
mmLoopInvariant1D in 2.5221548080444336 s

然而用C ++编写的相同优化给出了:

> Time [MM naive] 1.780587 s
> Time [MM invariant] 1.642554 s
> Time [MM loop IKJ] 0.304621 s
> Time [MM loop IKJ invariant] 0.276159 s

1 个答案:

答案 0 :(得分:2)

你的A, B and C矩阵是python列表,对于大维度来说速度很慢。您可以将它们创建为numpy数组,以尝试加速这些功能:

>>> import numpy as np
>>> A = np.random.uniform(-2., 2., (size,size))
>>> B = np.random.uniform(-2., 2., (size,size))

这将AB矩阵定义为size x size随机矩阵。

你的代码的瓶颈是python循环。 Python循环非常慢,并且您希望尽可能在代数运算中避免它们。在你的情况下,你想要实现的目标(如果我没有错)是C = A * B其中*是两个2D矩阵之间的点积。翻译为numpy:

>>> C = A.dot(B)

如果你计时:

>>> %timeit C = A.dot(B)
100 loops, best of 3: 7.91 ms per loop

仅需7.91 ms来计算点积。我知道你想玩python,但是如果你要进行数学微积分,你越早移动到numpy越好你的算法速度。

回到你的算法,如果我在你的计算机上运行你的代码,优化确实有效,并实现了一点加速:

mmNaive1D in 1.72708702087 s
mmInvariant1D in 1.64227509499 s
mmLoop1D in 1.57529997826 s
mmLoopInvariant1D in 1.26218104362 s

所以它对我有用。

编辑:在运行代码几次后,我得到了不同的结果:

mmNaive1D in 1.63492894173 s
mmInvariant1D in 1.1577808857 s
mmLoop1D in 1.67409181595 s
mmLoopInvariant1D in 1.32283711433 s

这次“优化”不起作用。我想这是因为算法的瓶颈是python循环,而不是内存访问。

EDIT2:一些更相关的结果,每个算法执行100次迭代以获得平均时间(花了一段时间哈哈):

mmNaive1D in 1.66692941904 s
mmInvariant1D in 1.15141540051 s
mmLoop1D in 1.58852998018 s
mmLoopInvariant1D in 1.28386260986 s

即使100次迭代仍然不够充足(如1.000.000会更好,但需要很长时间),它表明作为100次运行时的平均值,“优化”并不是真正的优化。我真的不知道为什么,但可能是在python中添加一个新变量比200次内存访问慢(可能后面有一些缓存)。