用矩阵乘向量的方式来优化两个10x10矩阵相乘的C代码,并分析优化后的 Cache 的命中率/不命中率
时间: 2023-06-13 18:02:40 浏览: 108
优化前的代码可能类似于以下形式:
```c
void matmul(int A[][10], int B[][10], int C[][10]) {
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
int sum = 0;
for (int k = 0; k < 10; ++k) {
sum += A[i][k] * B[k][j];
}
C[i][j] = sum;
}
}
}
```
可以使用矩阵乘向量的方式来优化这段代码,具体来说,可以将矩阵B进行转置,然后将每一行的元素依次存放到一个数组中,记为`B_row_major`。这样,计算矩阵乘积时,可以将矩阵B的每一列看作一个向量,然后用这个向量与矩阵A的每一行进行点积运算,得到矩阵C的一个元素。由于矩阵B的每一列对应于数组`B_row_major`的一个连续子序列,因此可以采用一维数组的方式来存储矩阵B,从而利用 CPU 缓存来提高访问效率。具体代码如下:
```c
void matmul_opt(int A[][10], int B[][10], int C[][10]) {
int B_row_major[100];
for (int j = 0; j < 10; ++j) {
for (int i = 0; i < 10; ++i) {
B_row_major[j*10+i] = B[i][j];
}
}
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
int sum = 0;
for (int k = 0; k < 10; ++k) {
sum += A[i][k] * B_row_major[j*10+k];
}
C[i][j] = sum;
}
}
}
```
接下来考虑优化后的代码的 Cache 命中率/不命中率。
由于矩阵A和矩阵B的大小都是10x10,因此它们的总大小为800字节。由于这两个矩阵是按行存储的,因此对它们的访问模式是局部性较好的,可以通过 CPU 缓存来提高访问效率。在计算矩阵乘积时,矩阵B被转置并存储到了一个一维数组`B_row_major`中。由于这个数组是按行存储的,因此对它的访问模式也是局部性较好的。在计算矩阵乘积时,每次取出一个矩阵A的一行和矩阵B的一列进行计算,因此矩阵A和矩阵B_row_major的访问模式也是局部性较好的。
考虑 CPU 的缓存结构,一般来说,它是由多级缓存组成的。以 Intel Core i7 8700K 为例,它的 L1 缓存大小为32KB,L2 缓存大小为256KB,L3 缓存大小为12MB。在这个例子中,由于数据量较小,可以将矩阵A、矩阵B、数组B_row_major和矩阵C全部存储到 L1 缓存中。因此,对这些数据的访问都可以在 L1 缓存中完成,命中率应该是非常高的,接近甚至等于100%。
综上所述,通过矩阵乘向量的方式来优化矩阵乘积的代码可以显著提高计算效率,并且在本例中,由于数据量较小,也可以获得非常高的缓存命中率。
阅读全文