以下代码将在具有以下缓存结构的CPU上运行:
L1缓存:1KB
二级缓存:8KB
L3缓存:64KB
区块大小:16B
unsigned int A[65536];
int i,j;
for (i=0 ; i<65536-256 ; i++)
for (j=1 ; j<128; j++)
A[i]=A[i]+A[i+j];
我正在为期中考试而学习有一个问题。修改此代码以最大限度地减少处罚次数。根据L1,L2和L3缓存计算缓存命中数和缓存未命中数。
我尝试通过Loop Interchange解决它。如果我改为下面的代码,那么将会有一个顺序访问,而不是每16,364个单词跨越内存。
unsigned int A[65536];
int i,j;
for (j=1 ; j<128; j++)
for (i=0 ; i<65536-256 ; i++)
A[i]=A[i]+A[i+j];
但我坚持使用缓存命中和未命中。 有人可以向我解释一下吗?
答案 0 :(得分:3)
假设unsigned int
是4个字节,则数组的大小为4 * 65536 = 256KB。您的L3缓存最多只能容纳64KB。
为了最大限度地减少惩罚,你应该做的第一件事是将循环分成4个子组,这样一旦你将一个条目加载到L3,你应该在被驱逐之前完全使用它。
unsigned int A[65536];
int i,j,k;
for (k=0 ; k<65536-256; k+=16384)
for (j=1 ; j<128; j++)
for (i=k ; i<MIN(k+16384,65536-256) ; i++) //define a MIN function to return minimum
A[i]=A[i]+A[i+j];
现在计算缓存命中和未命中,缓存行可以容纳4个数组条目。当您第一次访问A [0]时,它将是L1,L2和L3中的未命中。当从内存中取出时,你不只是获取A [0],你也可以获取A [1],A [2]和A [3],因为高速缓存行可以容纳所有4个。
在同一条指令中,您还可以访问A [i + j],在这种情况下将是A [1],这将是一个命中。就这样,
First iteration
A[i] - A[0] - Miss
A[i+j] - A[1] - Hit
Second Iteration
A[i] - A[1] - Hit
A[i+j] - A[2] - Hit
Third Iteration
A[i] - A[2] - Hit
A[i+j] - A[3] - Hit
Forth Iteration
A[i] - A[3] - Hit
A[i+j] - A[4] - Miss // This will cause to fetch A[4], A[5], A[6], A[7]
模式一直持续到L1被填满。