我想知道为什么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
答案 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))
这将A
和B
矩阵定义为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次内存访问慢(可能后面有一些缓存)。