我想用以下C代码进行块矩阵 - 矩阵乘法。在这种方法中,将块大小为BLOCK_SIZE的块加载到最快的缓存中,以减少计算期间的内存流量。
void bMMikj(double **A , double **B , double ** C , int m, int n , int p , int BLOCK_SIZE){
int i, j , jj, k , kk ;
register double jjTempMin = 0.0 , kkTempMin = 0.0;
for (jj=0; jj<n; jj+= BLOCK_SIZE) {
jjTempMin = min(jj+ BLOCK_SIZE,n);
for (kk=0; kk<n; kk+= BLOCK_SIZE) {
kkTempMin = min(kk+ BLOCK_SIZE,n);
for (i=0; i<n; i++) {
for (k = kk ; k < kkTempMin ; k++) {
for (j=jj; j < jjTempMin; j++) {
C[i][j] += A[i][k] * B[k][j];
}
}
}
}
}
}
我搜索了BLOCK_SIZE
的最合适值,我发现此处BLOCK_SIZE <= sqrt( M_fast / 3 )
和M_fast
是L1缓存。
在我的计算机中,我有两个L1缓存,如here所示,带有 lstopo
工具。
下面,我使用启发式方法,例如从BLOCK_SIZE
的{{1}}开始,4
的值增加8
次,使用不同的矩阵大小值。
跳跃以获得最佳MFLOPS(或乘法的最小时间)值和相应的1000
值将是最合适的值。
这是测试代码:
BLOCK_SIZE
测试为每个矩阵大小提供了不同的值,并且与公式不一致。计算机模型是“Intel®Core™i5-3320M CPU @ 2.60GHz×4”,3.8GiB,这里是{{3} }
另一个问题:
如果我有两个L1缓存,就像我在Intel specification中所拥有的那样,我应该考虑int BLOCK_SIZE = 4;
int m , n , p;
m = n = p = 1024; /* This value is also changed
and all the matrices are square, for simplicity
*/
for(int i=0;i< 1000; i++ , BLOCK_SIZE += 8) {
# aClock.start();
test_bMMikj(A , B , C , loc_n , loc_n , loc_n ,BLOCK_SIZE);
# aClock.stop();
}
关于其中一个或两者的总和吗?
答案 0 :(得分:0)
1。块矩阵乘法: 这个想法是通过重用当前存储在缓存中的数据块来最大程度地利用时间和空间局部性。您的相同代码是错误的,因为它仅包含5个循环; for block应该是6,例如:
for(int ii=0; ii<N; ii+=stride)
{
for(int jj=0; jj<N; jj+=stride)
{
for(int kk=0; kk<N; kk+=stride)
{
for(int i=ii; i<ii+stride; ++i)
{
for(int j=jj; j<jj+stride; ++j)
{
for(int k=kk; k<kk+stride; ++k) C[i][j] += A[i][k]*B[k][j];
}
}
}
}
}
为简单起见,最初将N和步幅都保持为2的幂。 ijk模式不是最佳选择,您应该选择kij或ikj,有关该here的详细信息。不同的访问模式具有不同的性能,您应该尝试ijk的所有排列。
2。块/步幅大小: 通常来说,在矩阵乘法的情况下,最快的缓存(L1)应该能够容纳3个数据块(步长*步长)以获得最佳性能,但是尝试并亲自找到它总是好的。 将步幅增加8可能不是一个好主意,请尝试将其保持为2的幂,因为大多数块大小都是以这种方式确定大小的。 而且您只应查看数据缓存(L1d),在您的情况下为32KB。