深入理解计算机系统 第六章 存储器层次结构
为什么要学习存储器的层次结构?
如果我们理解了系统是如何将数据在存储器层次结构中上上下下移动的,那么我们就可以编写自己的应用程序,使得它们的数据项存储在层次结构较高的地方,在那里 CPU 能更快地访问到它们。
局部性原理
计算机程序倾向于引用临近于其他最近引用过的数据项的数据项,或者最近引用过的数据项本身。这种倾向性,被称为局部性原理。
有良好局部性的程序比局部性差的程序运行地更快。且在现代计算机系统的各个层次,从硬件到操作系统、再到应用程序,它们的设计都利用了局部性。
缓存相关术语介绍
缓存命中
当程序需要第 k+1 层的某个数据对象 d 时,他首先在当前存储在第 k 层的一个块中查找 d。如果 d 刚好缓存在第 k 层中,那么就是我们所说的缓存命中。
缓存不命中
相对于缓存命中,如果第 k 层中没有缓存数据对象 d,那么就是我们所说的缓存不命中。
缓存不命中的种类
区分不同种类的缓存不命中有时候是很有帮助的。如果第 k 层的缓存是空的,那么对任何数据对象的访问都会不命中。一个空的缓存有时被称为冷缓存,此类不命中称为强制性不命中或冷不命中。
由于发生不命中的情况是很正常的,所以在不命中发生的时候,必须有对应的放置策略。而如果采用最灵活的方式(即允许第 k+1 层的任何块放在第 k 层的任何块中),成本会比较高,因为随机地放置块,定位起来代价很高。因此,实际情况中,被使用的通常是更为严格的放置策略,即使第 k+1 层的块对第 k 层的块有一种“多对一”的映射关系(第 k+1 层的某几个块只能映射到第 k 层的某一个块中),这就会导致一种由放置策略引起的不命中,我们称之为冲突不命中。
要编写缓存友好的代码,需要遵从以下两点原则:
1、让最常见的情况运行的快。程序通常把大部分时间都花在少量的核心函数上,而这些函数通常把大部分时间都花在了少量循环上。所以要把注意力集中在核心函数里的循环上,而忽略其他部分。
2、尽量减小每个循环内部的缓存不命中数量。在其他条件相同的情况下,不命中率较低的循环运行得更快。