我在 Java 中编写了两个矩阵类,只是为了比较它们的矩阵乘法的性能。一个类 (Mat1) 存储一个double[][] A
成员,其中i
矩阵的行是A[i]
。另一个类 (Mat2) 存储A
以及的转置T
在哪里。T
A
假设我们有一个方阵 M,我们想要 的乘积M.mult(M)
。致电产品P
。
当 M 是 Mat1 实例时,使用的算法很简单:
P[i][j] += M.A[i][k] * M.A[k][j]
for k in range(0, M.A.length)
在 M 是 Mat2 的情况下,我使用:
P[i][j] += M.A[i][k] * M.T[j][k]
这是相同的算法,因为T[j][k]==A[k][j]
. 在 1000x1000 矩阵上,第二个算法在我的机器上大约需要 1.2 秒,而第一个算法至少需要 25 秒。我期待第二个更快,但不是这么快。问题是,为什么速度这么快?
我唯一的猜测是第二个算法更好地利用了 CPU 缓存,因为数据以大于 1 个字的块的形式被拉入缓存,第二个算法通过仅遍历行从中受益,而第一个算法忽略了拉入的数据通过立即转到下面的行来缓存(在内存中大约有 1000 个字,因为数组是按行主要顺序存储的),没有任何数据被缓存。
我问过某人,他认为这是因为更友好的内存访问模式(即第二个版本会导致更少的 TLB 软故障)。我根本没有想到这一点,但我可以看出它是如何减少 TLB 错误的。
那么,它是什么?还是有其他原因导致性能差异?