在CSI-VII一篇中,我们了解了存储器系统的层次结构,并知道了层次结构自上而下使用了缓存(cashing)技术,因此我们着重介绍了存储系统中高速缓存的工作原理。本篇内容,我们通过分析几个代码实例来分析高速缓存如何影响程序,并提出如何编写高速缓存友好代码的方法。
代码实例
1. 首先让我们看第一个方法transpose,这是一个矩阵转置函数,定义如下:
Typedef int array[2][2];
Void transpose(array dst,array src)
{
Int I,j;
For(i=0;i<2;i++)
For(j=0;j<2;j++){
Dst[j][i]=src[i][j];
}
}
通过这个方法我们先看如何分析缓存的命中情况,在这之前我们假设代码运行的机器具有如下属性:
Sizeof(int)=4。
只有一个L1数据高速缓存,它是直接映射、直写、写分配的,块大小为8字节。
缓存大小为16个数据字节,初始为空。
Src数组地址从开始,dSt数组地址从16开始。
据此我们可以给出内存和缓存的映射关系:
根据这样的映射关系,我们可以看出每次对于读src或者写Dst数组,都会加载数据到缓存,并且刚好每行只能够放2个元素。例如当我们读元素src[0][0]时,会加载src[0][0],src[0][1]到同一高速缓存行中,这是有其在主存中的地址所决定的。同样,加载dst[0][0]时,也会将dst[0][1]加载进来。对于两个数组中的每个元素,下面的表格给出了其命中情况,m代表不命中,h代表命中。
Dst 数组 |
列1 |
列2 |
行1 |
m |
m |
行2 |
m |
m |
src 数组 |
列 1 |
列 2 |
行1 |
m |
m |
行2 |
m |
h |