我打算使用缓存友好的方法将2个矩阵相乘(这将导致更少的未命中)
我发现这可以通过缓存友好的转置函数来完成。
但我无法找到这个算法。我能知道如何实现这个目标吗?
答案 0 :(得分:4)
您正在寻找的词是颠簸。在Google yields more results中搜索颠簸矩阵乘法。
c = a * b的标准乘法算法看起来像
void multiply(double[,] a, double[,] b, double[,] c)
{
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
for (int k = 0; k < n; k++)
C[i, j] += a[i, k] * b[k, j];
}
基本上,在大步骤中快速导航内存对性能不利。 B [ k ,j]中 k 的访问模式正是如此。因此,我们可以重新排列操作,使大多数内部循环仅在矩阵的第二个访问索引上运行,而不是在内存中跳转:
void multiply(double[,] a, double[,] B, double[,] c)
{
for (i = 0; i < n; i++)
{
double t = a[i, 0];
for (int j = 0; j < n; j++)
c[i, j] = t * b[0, j];
for (int k = 1; k < n; k++)
{
double s = 0;
for (int j = 0; j < n; j++ )
s += a[i, k] * b[k, j];
c[i, j] = s;
}
}
}
这是该页面上给出的示例。但是,另一个选择是预先将B [k,*]的内容复制到数组中,并在内部循环计算中使用此数组。这种方法通常比其他方法快得多,即使它涉及复制数据。即使这看似违反直觉,也请随意尝试。
void multiply(double[,] a, double[,] b, double[,] c)
{
double[] Bcolj = new double[n];
for (int j = 0; j < n; j++)
{
for (int k = 0; k < n; k++)
Bcolj[k] = b[k, j];
for (int i = 0; i < n; i++)
{
double s = 0;
for (int k = 0; k < n; k++)
s += a[i,k] * Bcolj[k];
c[j, i] = s;
}
}
}
答案 1 :(得分:1)
@ Cesar的回答不正确。例如,内循环
for (int k = 0; k < n; k++)
s += a[i,k] * Bcolj[k];
通过a。的第i列。
以下代码应确保我们始终逐行访问数据。
void multiply(const double (&a)[I][K],
const double (&b)[K][J],
double (&c)[I][J])
{
for (int j=0; j<J; ++j) {
// iterates the j-th row of c
for (int i=0; i<I; ++i) {
c[i][j] = 0;
}
// iterates the j-th row of b
for (int k=0; k<K; ++k) {
double t = b[k][j];
// iterates the j-th row of c
// iterates the k-th row of a
for (int i=0; i<I; ++i) {
c[i][j] += a[i][k] * t;
}
}
}
}