缓存不命中率的计算
一台具有块大小为16字节,整个大小为1024字节的高速缓存:
struct algea_position(){
int x;
int y;
};
struct algea_position grid[16][16];
int total_x=0, total_y=0;
int i,j;
Q u e s t i o n 1 Question\ 1 Question 1
for(i=0;i<16;i++)
for(j=0;j<16;j++)
total_x += grid[i][j].x;
for(i=0;i<16;i++)
for(j=0;j<16;j++)
total_y += grid[i][j].y;
不命中率是多少?
比如看第一个双重循环,每次读一次数据都会使一行的块给填满,如下:
g r i d [ i ] [ j ] . x grid[i][j].x grid[i][j].x | g r i d [ i ] [ j ] . y grid[i][j].y grid[i][j].y | g r i d [ i ] [ j + 1 ] . x grid[i][j+1].x grid[i][j+1].x | g r i d [ i ] [ j + 1 ] . y grid[i][j+1].y grid[i][j+1].y |
---|
在最开始存在冷不命中率,会出现第一次不命中,第二次命中的现象;并且这个现象是不断地交替的出现,同样的对于第二个双层循环也是一样的。因此它的不命中率是50%。
Q
u
e
s
t
i
o
n
2
Question\ 2
Question 2
for(i=0;i<16;i++)
for(j=0;j<16;j++){
total_x += grid[j][i].x;
total_y += grid[j][i].y;
}
不命中率?
这里的关键是注意到这个高速缓存只能保存数组的1/2,因此按照列顺序来扫描数组的第二部分会驱逐扫描第一部分加载进来的那些行。比如读
g
r
i
d
[
8
]
[
0
]
grid[8][0]
grid[8][0]会覆盖掉
g
r
i
d
[
0
]
[
0
]
g
r
i
d
[
0
]
[
1
]
grid[0][0]\ grid[0][1]
grid[0][0] grid[0][1],所以开始扫描下一列的时候,对
g
r
i
d
[
0
]
[
1
]
grid[0][1]
grid[0][1]会发生不命中;另外你实际上从另外要给角度来看,循环内部有两个操作,第一个引用
x
x
x的操作由于是按列同时高速缓存大小不够,那么其必定不命中,后面对
y
y
y的引用就一定是命中的,所以和前面一样的,会出现不命中、命中、不命中、命中的这种交替的现象,所以最终也可以得出它的不命中率其实就是50%
Q
u
e
s
t
i
o
n
3
Question\ 3
Question 3
for(i=0;i<16;i++)
for(j=0;j<16;j++){
total_x += grid[i][j].x;
total_y += grid[i][j].y;
}
不命中率?
书上的解释:这个循环有很好的步长为1的引用模式,因此所有的不命中都是最开始的冷不命中。
其实我对这个观点并不是特别的认同,由于高速缓存大小不够因此会出现不是冷不命中的情况(不然当你引用把之前所有的空的缓存全都占用了,下次还需要引用的时候还是覆盖空的?),所以还是去分析循环内部,画出块在引用后发生的变化:
g r i d [ i ] [ j ] . x grid[i][j].x grid[i][j].x | g r i d [ i ] [ j ] . y grid[i][j].y grid[i][j].y | g r i d [ i ] [ j + 1 ] . x grid[i][j+1].x grid[i][j+1].x | g r i d [ i ] [ j + 1 ] . y grid[i][j+1].y grid[i][j+1].y |
---|
第一次引用 x x x的时候会发生缓存不命中,但第一次引用的 y y y,以及第二次引用的 x y x\ y x y都会命中,也就是每四次的引用都会发生一次的不命中,那么不命中率就是25%
总结:其实这个问题我觉得都可以通过画它的块结构在引用前后的变化以及分析若干次引用的不命中次数,这个不命中次数占引用次数的比值就是不命中率。
以上仅是个人理解,或许有失偏颇,欢迎品论下方讨论!