您的位置:首页 > 其它

gem5验证cache的不同映像方式对cache命中率的影响

2015-01-29 11:06 218 查看
陆续写些关于新书《自己动手写CPU》的博客,本篇主要是讲解 gem5验证cache的不同映像方式对cache命中率的影响。

cache的基本在http://blog.csdn.net/leishangwen/article/details/30049469中已有介绍,此处不再重复,只是简单介绍一下。

主要由三大部分组成:

  Cache存储体:存放由主存调入的指令与数据块。
  地址转换部件:建立目录表以实现主存地址到缓存地址的转换。
  替换部件:在缓存已满时按一定策略进行数据块替换,并修改地址转换部件。

地址映象:是指某一数据在内存中的地址与在缓冲中的地址,两者之间的对应关系。有三种地址映象的方式。

1、全相联方式

  地址映象规则:主存的任意一块可以映象到Cache中的任意一块

  (1) 主存与缓存分成相同大小的数据块。

  (2) 主存的某一数据块可以装入缓存的任意一块空间中。

2、直接相联方式

  地址映象规则: 主存储器中一块只能映象到Cache的一个特定的块中。

  (1) 主存与缓存分成相同大小的数据块。

  (2) 主存容量应是缓存容量的整数倍,将主存空间按缓存的容量分成区,主存中每一区的块数与缓存的总块数相等。

  (3) 主存中某区的一块存入缓存时只能存入缓存中块号相同的位置。

3、组相联映象方式

  组相联的映象规则:

  (1) 主存和Cache按同样大小划分成块。

  (2) 主存和Cache按同样大小划分成组。

  (3) 主存容量是缓存容量的整数倍,将主存空间按缓冲区的大小分成区,主存中每一区的组数与缓存的组数相同。

  (4) 当主存的数据调入缓存时,主存与缓存的组号应相等,也就是各区中的某一块只能存入缓存的同组号的空间内,但组内各块地址之间则可以任意存放, 即从主存的组到Cache的组之间采用直接映象方式;在两个对应的组内部采用全相联映象方式。

下面通过试验验证cache的不同映像方式对cache命中率的影响。采用的CPU模型是timeing,默认的dcache是64KB,缓存line大小是64B。观察下面的程序:

int main()
{

int i=0;
long count=0;
long temp[4096][8];

for(i=0;i<1000;i++)
{
 temp[0][0]=count++;   // 循环访问0,1024,2048
temp[1024][0]=count++;
temp[2048][0]=count++;
}
}
有一个二维数组temp,在C语言中,二维数组在内存中是按照行的次序在按照列的次序存放,也就是说,二维数组中同一行的数据在内存中是挨着的,但同一列的数据在内存中是分散的。所以,这个temp数组刚好对应4096个cache行,而数据cache实际只有64KB,共1024行,也就是说,这个数组中有多个元素会映射到cache的同一行,比如:temp[0][0]、temp[1024][0]、temp[2048][0]都会映射到cache的同一行。

上面的程序中,如果cache采用的是直接映射方式,那么会有大约1000*3次cache冲突,每次冲突都会替换cache,导致大约1000*3次cache reload,也会有大约1000*3次cache miss。

如果采用的是4路组相联的映射方式,那么temp[0][0]、temp[1024][0]、temp[2048][0]会被映射到cache不同的组中,从而避免了cache冲突,也就不会发生那么多次cache miss、cache reload,大约只有3次。

还是以我的开发环境为例,gem5安装在/root/gem5/gem5-stable-50ff05095970/路径下,修改tests/test-progs/hello/src目录下的hello.c,代码就是上面的代码。

使用如下命令编译(需要在/root/gem5/gem5-stable-50ff05095970/tests/test-progs/hello/src目录下执行下面的命令):

gcc --static hello.c -o hello


然后使用前几次编译得到的X86对应的gem5.opt运行hello,并且选择CPU模型是Timing,也就是顺序流水线模型,命令如下(需要在/root/gem5/gem5-stable-50ff05095970/路径下执行下面的命令),其中有一个参数l1d_assoc=1,该参数制定了相联的组数为1,也就是直接相联方式。

./build/X86/gem5.opt ./configs/example/se.py --cpu-type=timing --caches --l1d_assoc=1 -c ./tests/test-progs/hello/src/hello


得到的统计结果在m5out/stats.txt中,找到其中的如下数据(数据cache写操作的缺失数):

system.cpu.dcache.WriteReq_misses::cpu.data         3092


再使用如下命令重新运行程序,其中参数l1d_assoc=4,该参数制定了相联的组数为4,也就是4路组相联方式。

./build/X86/gem5.opt ./configs/example/se.py --cpu-type=timing --caches --l1d_assoc=4 -c ./tests/test-progs/hello/src/hello
得到的统计结果在m5out/stats.txt中,找到其中的如下数据(数据cache写操作的缺失数):

system.cpu.dcache.WriteReq_misses::cpu.data           93


从实验可知,对于测试程序而言,在4路组相联的cache下,可以大大减少cache miss的次数。

在实验中,产生了一个问题,百思不得其解,还盼哪位大神指点指点。问题是这样的,我们看下面的程序:

int main()
{

int i=0;
long count=0;
long temp[4096][8];

for(i=0;i<1000;i++)
{
temp[0][0]=count++;   // 循环访问0,1024,2048,3072
temp[1024][0]=count++;
temp[2048][0]=count++;
         temp[3072][0]=count++;     // 比上面的测试程序多了这么一个赋值操作
      }
}


temp[0][0]、temp[1024][0]、temp[2048][0]、temp[3072][0]都会映射到cache的同一行,但是如果采用4路相联的cache,那么这四个值应该会被映射到不同的组中,这样按理说不会发生冲突,cache miss的次数大约是4次,但是实验结果显示是4000多次,不知道是什么原因。盼大神给解答。

PS:找到原因了,在上面的代码中,变量i、count有可能与temp[0][0]也映射到cache的同一行,因为这几个变量是连续定义的,这样在后面的for循环中,每轮循环都会有多与4个变量被写到cache的同一行,这样即使将cache的路数设置为4,也还是会产生缺失,解决办法是在count的定义后面插入64个字节的空当,如下:

int main()
{

int i=0,j=0;
long count;
long pading[8];                // 插入64字节的空当
long temp[4096][8];

for(i=0;i<1000;i++)
{
temp[0][0]=count++;
temp[1024][0]=count++;
temp[2048][0]=count++;
temp[3072][0]=count++;
}
}


这样的得到的实验结果就是符合预期的,分别在数据cache的路数为1、2、4时运行上面的程序,可以发现路数为为4时将显著降低数据cache的miss次数,大约降低了1000倍,符合理论分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: