CSSAPP稀里糊涂的读书笔记(六)存储器层次结构
本章主要讲解存储器的层次结构,因为它对应用程序的性能有着巨大的影响。主要是围绕着局部性(locality)这个基本属性展开。
-
随机访问存储器(Random-Access Memory,RAM)分为两类:静态的和动态的。静态RAM(SRAM)比动态RAM(DRAM)更快,也贵的多。SRAM用来作为高速缓存。
-
磁盘是广为应用的保存大量数据的存储设备,存储数据的数量级可以达到几百到几千 千兆字节,而基于RAM的存储器只能有几百或几千兆字节。不过,从磁盘上读信息的时间为毫秒级,比从DRAM读慢了10万倍,比从SRAM读慢了100万倍。
-
磁盘以扇区大小的块来读写数据。对扇区的访问时间有三个主要的部分:寻道时间、旋转时间和传送时间。
-
DMA传送:设备以自己执行读或者写总线事务而不需要CPU干涉的过程,称为直接内存访问(Direct Memory Access)。
-
局部性原理:一个拥有良好局部性的计算机程序,它倾向于引用邻近于其他最近引用过的数据项的数据项,或者最近引用过的数据项本身。
时间局部性:被引用过一次的内存位置很可能在不远的将来再被多次引用。
空间局部性:如果一个内存位置被引用了一次,那么程序很可能在不远的将来引用附近的一个内存位置。
一般而言,有良好局部性的程序比局部性差的程序运行得更快。
- 重复引用相同变量得程序有良好得时间局部性。
- 对于具有步长为k得引用模式得程序,步长越小,空间局部性越好。
- 对于取指令来说,循环有好得时间和空间局部性。循环体越小,循环迭代次数越多,局部性越好。
-
存储器层次结构
-
一般而言,高速缓存是一个小而快速的存储设备,它作为存储在更大、也更慢的设备中的数据对象的缓冲区域。
存储器层次结构的中心思想是,对于每个k,位于k层的更快更小的存储设备作为位于k+1层的更大更慢的存储设备的缓存。换句话说,层次结构中的每一层都缓存来自较低一层的数据对象。 -
缓存命中:当程序需要第k+1层的某个数据对象d时,它首先在当前存储在第k层的一个块中查找d。如果d刚好缓存在第k层中,那么就是我们所说的缓存命中。
缓存不命中:如果第k层中没有缓存数据对象d,那么就是我们所说的缓存不命中。当发生缓存不命中时,第k层的缓存从第k+1层缓存中取出包含d的那个块,如果第k层的缓存已经满了,可能就会覆盖现存的一个块。 -
早期计算机系统的存储器层次结构只有三层:CPU寄存器、DRAM主存储器和磁盘存储。不过由于CPU和主存之间逐渐增大的差距,系统设计者*在CPU寄存器文件和主存之间插入了一个小的SRAM高速缓存存储器,称为L1高速缓存。L1高速缓存的访问速度几乎和寄存器一样快,典型的是大约4个时钟周期。
之后又在L1高速缓存和主存之间又插入了一个更大的高速缓存,称为L2高速缓存,可以在大约10个时钟周期内访问到它。有些还包括一个更大的告诉缓存,称为L3高速缓存,大约50个周期内访问到。 -
根据每个组的高速缓存行数E,高速缓存被分为不同的类。每个组只有一行(E=1)的高速缓存称为直接映射高速缓存。
-
术语“抖动”描述的是这样一种情况,即高速缓存反复地加载和驱逐相同的高速缓存块的组。
-
一个 1<E<C/B 的高速缓存通常称为E路组相联高速缓存。
-
全相联高速缓存是由一个包含所有高速缓存行的组(即 E=C/B)组成的。
-
假设我们要写一个已经缓存了的字w。在高速缓存更新了它的w的副本之后,怎么更新w在层次结构中紧接着低一层中的副本呢?最简单的方法,称为直写,就是立即将w的高速缓存块写回到紧接着的低一层中。缺点就是每次写都会引起总线流量。另一种方法,称为写回,尽可能地推迟更新,只有当替换算法要驱逐这个更新过的块时,才把它写到紧接着的低一层中。缺点是增加了复杂性,需要维护一个额外的修改位。
另一个问题是如何处理写不命中。一种方法,称为写分配,加载相应的低一层中的块到高速缓存中,然后更新这个高速缓存块。缺点是每次不命中都会导致一个块从低一层传送到高速缓存。另一种方法,称为非写分配,避开高速缓存,直接把这个字写到低一层中。
直写高速缓存通常是非写分配的。写回高速缓存通常是写分配的。 -
编写高速缓存友好的代码:
- 对局部变量的反复引用是好的,因为编译器能够将它们缓存在寄存器文件中(时间局部性)。
- 步长为1的引用模式是好的,因为存储器层次结构中所有层次上的缓存都是将数据存储为连续的块(空间局部性)。