Caching cpumemory.pdf-缓存优化矩阵乘法

Caching cpumemory.pdf-缓存优化矩阵乘法,caching,memory,optimization,matrix,cpu,Caching,Memory,Optimization,Matrix,Cpu,我在看报纸 Ulrich Drepper和我无法理解下面关于优化的部分 第6.2.1章(第49-50页)矩阵乘法中的缓存访问: 矩阵乘法的第一种简单方法如下所示: for (i = 0; i < N; ++i) for (j = 0; j < N; ++j) for (k = 0; k < N; ++k) res[i][j] += mul1[i][k] * mul2[k][j]; 现在很明显,若缓存线的宽度是2个双倍值,那个么它

我在看报纸 Ulrich Drepper和我无法理解下面关于优化的部分 第6.2.1章(第49-50页)矩阵乘法中的缓存访问:

矩阵乘法的第一种简单方法如下所示:

for (i = 0; i < N; ++i)
    for (j = 0; j < N; ++j)
        for (k = 0; k < N; ++k)
            res[i][j] += mul1[i][k] * mul2[k][j];
现在很明显,若缓存线的宽度是2个双倍值,那个么它将完全变宽 利用。但乌尔里希接着说:

继续这一思想,有效地使用res矩阵,即 同时写8个结果,我们应该将外循环展开8次 嗯

为了简洁起见,我只再次展开了外环两次

for (i = 0; i < N; i += 2)
    for (j = 0; j < N; j+=2)
        for (k = 0; k < N; ++k) {
            res[i+0][j+0] += mul1[i+0][k] * mul2[k][j+0];
            res[i+0][j+0] += mul1[i+0][k] * mul2[k][j+0];
            res[i+1][j+0] += mul1[i+1][k] * mul2[k][j+0];
            res[i+1][j+1] += mul1[i+1][k] * mul2[k][j+1];
        }
(i=0;i 对于(j=0;j 对我来说,它似乎比以前的版本更糟糕,因为现在可以访问
mul1

按列。请解释一下Ulrich的意思。

缓存中有三个矩阵:左输入、右输入和结果

原始代码可以很好地访问左边的输入,因为它是行主循环,最里面的循环增加k,所以它沿着缓存线行进。。通过单个展开可以很好地访问第二个矩阵,因为现在缓存线中的所有列都是在移出缓存线之前使用的

问题是结果矩阵。。它也是row-major,但是缓存线是由j索引的,而不是由k索引的。。你是对的。。j已经展开,因此它使用结果矩阵中缓存线上的所有元素。。因此,第二次展开似乎没有任何收获。。它所做的只是添加两条额外的缓存线。。左矩阵和结果矩阵各有一个!它不会提高任何缓存线元素的覆盖率

但是,它确实会重复使用右矩阵的缓存线两次。。这减少了必须引入右矩阵行的总次数。。而且它不会增加引入左右矩阵缓存线的次数。。因此,也许整个生产线的重复使用才是优势所在。。我想问题是,这是否被正确地阻止为缓存大小,以及缓存的设置关联性是什么。。如果所有三个矩阵的所有行都保留在缓存中,则这没有任何优势。。(但这并没有让事情变得更糟!)

for (i = 0; i < N; i += 2)
    for (j = 0; j < N; j+=2)
        for (k = 0; k < N; ++k) {
            res[i+0][j+0] += mul1[i+0][k] * mul2[k][j+0];
            res[i+0][j+0] += mul1[i+0][k] * mul2[k][j+0];
            res[i+1][j+0] += mul1[i+1][k] * mul2[k][j+0];
            res[i+1][j+1] += mul1[i+1][k] * mul2[k][j+1];
        }