首页 > 代码库 > gem5验证cache的不同映像方式对cache命中率的影响

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

陆续写些关于新书《自己动手写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多次,不知道是什么原因。盼大神给解答。

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