哪种访问模式对于编写最大限度利用数据数据位置的缓存高效的外部产品类型代码最有效?
考虑用于处理两个数组的所有元素对的代码块,例如:
for (int i = 0; i < N; i++)
for (int j = 0; j < M; j++)
out[i*M + j] = X[i] binary-op Y[j];
当binary-op
是标量乘法且X
和Y
为1d时,这是一个标准的矢量矢量外积,但同样的模式也是X
时的矩阵乘法Y
是矩阵,binary-op
是两个矩阵的i
行和j
列之间的点积。
对于矩阵乘法,我知道像OpenBLAS和MKL这样的优化BLAS可以比上面的双循环样式代码获得更多更高的性能,因为它们以如下方式处理元素:更多地利用CPU缓存。不幸的是,OpenBLAS内核是用汇编语言编写的,因此很难弄清楚它是怎么回事。
是否有任何良好的交易技巧&#34;重组这些类型的双循环以提高缓存性能?
由于out
的每个元素只被击中一次,我们可以清楚地自由重新排序迭代。 out
的直线性遍历是最容易编写的,但我认为它不是最有效的执行模式,因为您不会利用{{1}中的任何位置}}
我对X
和M
较大的设置特别感兴趣,并且每个元素(N
和X[i]
)的大小相当小(如O(1)字节),所以讨论的东西类似于矢量 - 矢量外积或高和瘦矩阵乘以短和脂肪矩阵(例如Y[j]
乘以N x D
其中D x M
很小。)
答案 0 :(得分:6)
对于足够大的M
,Y
向量将超过L1缓存大小。 * 因此,在每个新的外部迭代中,您将重新加载{{1}来自主内存(或至少是较慢的缓存)。换句话说,您不会在Y
中利用时间局部性。
您应该阻止访问Y
;像这样的东西:
Y
以上对for (jj = 0; jj < M; jj += CACHE_SIZE) { // Iterate over blocks
for (i = 0; i < N; i++) {
for (j = jj; j < (jj + CACHE_SIZE); j++) { // Iterate within block
out[i*M + j] = X[i] * Y[j];
}
}
}
的访问不起任何作用,但新值只能经常访问X
,因此影响可能微乎其微。
<小时/> *如果一切都小到已经适合缓存,那么你不能做得比现有的更好(尽管有矢量化机会)。