如前所述,我正在做一些矩阵乘法基准测试。
Why is MATLAB so fast in matrix multiplication?

现在,我又遇到了另一个问题,当将两个2048x2048矩阵相乘时,C#与其他矩阵有很大的不同。当我尝试仅乘以2047x2047矩阵时,这似乎很正常。也添加了一些其他内容进行比较。

1024x1024-10秒。

1027x1027-10秒。

2047x2047-90秒。

2048x2048-300秒。

2049x2049-91秒。 (更新)

2500x2500-166秒

对于2k x 2k的情况,这是三分半钟的差异。

使用2dim数组

//Array init like this
int rozmer = 2048;
float[,] matice = new float[rozmer, rozmer];

//Main multiply code
for(int j = 0; j < rozmer; j++)
{
   for (int k = 0; k < rozmer; k++)
   {
     float temp = 0;
     for (int m = 0; m < rozmer; m++)
     {
       temp = temp + matice1[j,m] * matice2[m,k];
     }
     matice3[j, k] = temp;
   }
 }

最佳答案

这可能与L2缓存中的冲突有关。

matice1上的缓存未命中不是问题,因为它们是按顺序访问的。
但是对于matice2,如果一个完整的列适合L2(即,当您访问matice2 [0,0],matice2 [1,0],matice2 [2,0]等时,什么都不会被驱逐),那么就没有问题了用matice2缓存未命中。

现在更深入地了解缓存的工作原理,如果变量的字节地址为X,则其缓存行将为(X >> 6)&(L-1)。其中L是缓存中缓存行的总数。 L始终是2的幂。
六个来自以下事实:2 ^ 6 == 64字节是高速缓存行的标准大小。

现在这是什么意思?好吧,这意味着如果我有地址X和地址Y并且
(X >> 6)-(Y >> 6)可被L整除(即2的大幂),它们将存储在同一缓存行中。

现在回到您的问题,2048和2049有什么区别,

当您的大小为2048时:

如果采用&matice2 [x,k]和&matice2 [y,k],则差异(&matice2 [x,k] >> 6)-(&matice2 [y,k] >> 6)将被2048 * 4除(大小的 float )。因此2的大方。

因此,根据L2的大小,您将有很多缓存行冲突,并且仅利用L2的一小部分来存储列,因此您实际上将无法在缓存中存储完整的列,因此会导致性能下降。

当size为2049时,差异为2049 * 4(不是2的幂),因此您的冲突将更少,并且您的列将安全地放入缓存中。

现在,要检验此理论,您可以做一些事情:

像matice2 [razmor,4096]这样分配数组matice2数组,并以razmor = 1024、1025或任何大小运行,与以前相比,您会发现性能很差。这是因为您强制对齐所有列以使其相互冲突。

然后尝试使用matice2 [razmor,4097]并以任意大小运行它,您应该会看到更好的性能。

10-07 16:39