缓存是20世纪最大的发明,其原理用一些存取速度较快的存储设备做为数据缓冲区,避免频繁访问速度较慢的低速存储设备,归根结底的原因是,低速存储设备是整个系统的瓶颈,缓存用来缓解“瓶颈设备”的压力。
之前介绍实模式下的寄存器时,举了一个浏览器访问网页的例子,里面有12步,几乎步步都用到了缓存。不过,这些缓存都是在内存DRAM中实现的,即动态随机访问存储器,究其原因是数据要么在数据库中要么在硬盘上,其速度肯定比内存慢。选做用缓存的存储设备,其存取速度肯定是比其原有存储设备更快,否则失去了缓存的意义。相对于cpu来说,DRAM太慢了,如果也要用它来做cpu的缓存,反而是拖了后腿,不如不用。
cpu为什么要用缓存?因为待执行的指令和相关数据是存储在低速的内存中,让cpu这种高速设备等待慢速的内存,着实太浪费cpu资源了。人们根本无法容忍cpu如此“漫长”的“浪费”,所以需要用一个比内存更快的存取设备做缓冲区,尽量和cpu一个速度,让cpu不要等待。于是SRAM成了cpu的救世主,成为cpu和内存之间数据缓存的不二之选。
前面在介绍实模式下的寄存器时,也说到了cpu中的缓存。cpu中有一级缓存L1、二级缓存L2,甚至三级缓存L3等。它们都是SRAM,即静态随机访问存储器,它是最快的存储器啦。之所以把SRAM和寄存器放到一块说,是因为很多同学在感观上觉得寄存器是cpu直接使用的存储单元,所以寄存器比SRAM更快。其实他们在速度上是同一级别的东西,因为寄存器和SRAM都是用相同的存储电路实现的,用的都是触发器,它可是工作速度极快的,属于纳秒级别。至于触发器是什么,这已属于硬件范畴,这里咱就不深究了,因为我也不懂,不敢乱说啦^_^,有兴趣的同学请自行调研吧。
有哪些东西可以被缓存呢?无论是程序中的数据或指令,在cpu眼里全是一样形式的二进制01串,没有任何区别,都是cpu待处理的“数据”。所以我们眼中的指令和数据都可以被缓存到SRAM中。
什么时候能缓存呢?可以根据程序的局部性原理采取缓存策略。局部性原理是:程序90%的时间都运行在程序中10%的代码上。
局部性分为两个方面:
一方面是时间局部性:最近访问过的指令和数据,在将来一段时间内依然经常被访问。
另一方面空间局部性:靠近当前访问内存空间的内存地址,在将来一段 时间也会被访问。
举一个典型的例子,我们在用高级语言写程序时,经常会写到这样的循环嵌套代码,如:
int array[100][100];
int sum = 0;
…
数组array元素被赋值,略。
…
for (int i=0, i<100,i++) {for(int j=0;j<100,j++) {sum+=array[i][j];}
}
以上是将二维数组中的所有元素相加求和。循环中经常被用到的地址是sum所在的地址,经常被用到的指令是加法求和指令,这是在时间上的局部性。未来要访问的地址是与当前访问地址&array[i][j]相邻的地址&array[i][j+1],它们之间只差一个整型变量的大小,这是空间上的局部性的。(当然,这些局部性都是编译器编译的结果,编译器就是这样安排的。)cpu利用此特性,将当前用到的指令和当前位置附近的数据都加载到缓存中,这就大大提高了cpu效率,下次直接从缓存中拿数据,不用再去内存中取啦。
当然,上面说的是理想的状态,如果缓存中没有相应的数据,还是要去内存中去加载,然后再放到缓存中。