《现代体系结构上的UNIX系统:内核程序员的对称多处理和缓存技术(修订版)》——2.3 直接映射高速缓存

2.3 直接映射高速缓存

最简单的高速缓存组织就是直接映射(direct mapped)或者单路组相联(one-way set associative)高速缓存(短语“单路组相联”的意思在下一节介绍“双路组相联”的时候就清楚了)。TI MicroSPARC的片上数据高速缓存使用的就是这种高速缓存组织。它包含有2 KB的数据高速缓存,组织成128个高速缓存行,每行16字节。在如图2-5所示的这种类型的高速缓存中,在保存数据的部分高速缓存里,以散列算法用地址计算出每行有且仅有的一个索引(也就是说,直接映射关系)。高速缓存可以当作是高速缓存行的线性数组,这些行都以散列算法计算得出的结果作为索引。在搜索期间,索引行中的标记要和地址进行比较以发现是否命中。因此,单有索引还是不够的,因为采用任何类型的散列算法都会有许多不同地址计算出相同的索引结果。在出现一次命中的时候,就从高速缓存行中提取数据,送往CPU。如果标记和地址不匹配,或者有效位没有打开(要记住有效位是和地址一起保存在标记中的控制信息),那么就出现一次缺失,因为在直接映射的高速缓存中,这是能够保存数据的唯一位置。因此没有必要进一步搜索高速缓存。

使用直接映射高速缓存机制的其他处理器有Intel Pentium,它用于其外部高速缓存,以及MIPS R2000/R3000/R4000所支持的所有高速缓存。

2.3.1 直接映射高速缓存的散列算法
直接映射高速缓存的高速缓存散列算法必须将一个来自CPU的给定地址转换为高速缓存中一行的索引值。因为散列算法的计算是在硬件中完成的,所以它必须既简单,速度又快。对于降低成本和获得快速实现来说,简单性是必不可少的。速度非常重要,因为需要索引在适当的高速缓存行上启动读取周期。在比较高速缓存标记,查明是否有一次命中之前,必须完成上面所有的步骤。由于有这些限制因素,所以在高速缓存中使用的散列算法很少会采用算术运算,因为这些操作会花费很长的时间。

最常用的高速缓存散列算法是取模(modulo)函数,这种函数利用了这样的事实,即在直接映射高速缓存中的行数往往是2的幂。这就使得散列算法只要从地址中选出若干位,选出的位数正好等于高速缓存行数以2为底的对数,就能直接用它们来作为索引。例如,考虑TI MicroSPARC上的数据高速缓存,它有128(27)行,每行16字节。这个配置的散列算法应当从地址中选出“位<10..4>”(从第4到第10个位),使用这7个位从高速缓存的128行中选出一行(参见图2-6)。因为每一行高速缓存有16字节,所以该地址的低4位将选择CPU寻址的高速缓存行中的若干字节。因为高速缓存总共有2 KB,所以我们能够看到这种选择索引的方法会让所有模2048的地址都索引到高速缓存中相同的行(所有和“位<10..4>”匹配的地址都生成相同的索引)。这叫做取模散列算法(modulo hashing algorithm)。

此外,产生相同索引的地址被认为有相同的颜色。如果一个高速缓存的大小是页面大小的倍数,则该高速缓存就被认为其颜色和高速缓存中的页数一样多。如果一组存储页面的地址散列到高速缓存内同一组的高速缓存行上,那么称这组存储页面有相同的颜色。高速缓存颜色是一种用来区分页面如何索引高速缓存的概念。它的用途将在7.2.3节中进一步讨论(参见图7-8,该图给出了相对于存储器中页面的高速缓存颜色)。

图2-7描述了程序的地址空间是怎样映射到高速缓存的存储器上的。因为在“位<10..4>”中拥有相同位模式的所有地址都会索引到高速缓存中相同的行,所以地址0、2048(2 KB)、4096(4 KB)、6144(6 KB)等都将索引或者映射到高速缓存中的第0行,而地址16、2064(2 KB + 16)、4112(4 KB + 16)、6160(6 KB + 16)等都映射到第1行上。正如2.2.1节所阐述的那样,标记将会解决歧义的问题,因为它们指出了被高速缓存的数据的地址。

虽然取模散列算法是最常用的方法,但是用来选择高速缓存行的位可以取自地址中的任何部分。为了看到取模散列方法的优点,下面的例子将使用如图2-8所示的另一种散列算法。这两种散列算法之间的区别在于程序中的地址是如何索引高速缓存的。在第一个例子(图2-6)中,连续的程序地址映射到高速缓存中的连续位置,而图2-8中的算法则导致那些在“位<18..12>”中有相同值的整个地址范围都映射到相同的高速缓存行上。因此从0x0到0xfff的所有地址都索引到高速缓存中的第0行,而从0x1000到0x1fff的所有地址都索引到第1行,依此类推。效果如图2-9所示。

虽然实现这样的映射关系是可能的,但是我们不使用它,因为它导致了高速缓存利用率的低下(这里只给出举例说明)。例如,完全处于从0x0到0x1fff地址范围内的一个小程序让其所有的引用都映射到了高速缓存的头两行上。当这个程序正在执行的时候,只使用了2 KB高速缓存中的32字节。在这样一种情况下,可能会有很低的命中率,而从高速缓存感觉不出来性能增益。

通过对比,读者可以看到图2-6所示的散列算法提供了一种地址到高速缓存的良好映射关系,能够把空间局部性扩展到最大。出于这个原因,它就是所有使用散列算法的高速缓存存储系统而选择的算法。

2.3.2 直接映射高速缓存的实例
现在我们将迄今为止所介绍的所有概念联系起来,以展示一个查找操作如何发生的完整例子。对于这个例子来说,假定我们有一个字长为4字节、带有12位地址的系统。系统包含的直接映射高速缓存每行8字节,共计8行(为了简化示例,选择了12位的地址和一小块高速缓存。注意,始终都使用八位组的记法)。因为一行中有8字节,意味着以“位<2..0>”选择高速缓存行内的字节,而仅以第2个位就能选择行内的字,因为每行有8字节,即2个字。根据前面的讨论,最好的散列算法是使用“位<5..3>”作为高速缓存行的索引。索引一个8行的高速缓存需要3位,直接选择上述的几位选择高速缓存行内的字节,这样做能基于局部引用来充分利用高速缓存。地址中剩下的位则用于和高速缓存的标记进行比较(参见图2-10)。

对于这个例子来说,我们假定高速缓存的初始内容如图2-11所示。在这个配置中,标记部分的符号“---”表示这一行无效。在高速缓存行中数据部分的两个字被一条竖线分隔开了。在高速缓存行中的字采用高字节结尾的顺序,这意味着地址最小的字位于高速缓存行的左端。

举第一个例子,CPU将发出一次读取地址01234的操作。散列算法选择地址中的“位<5..3>”,它们给该地址一个行索引值3(参见图2-12)。

因为这是一个直接映射高速缓存,所以这个索引只和一行有关,如果这个地址的数据都在高速缓存中,那么数据就保存在这一行里。高速缓存控制硬件获得索引值,并且读取高速缓存行3的内容。它发现有一个有效标记,并且把这个标记同地址中的标记部分(位<11..6>,即012)相比较。地址中的标记部分和被索引的高速缓存行中的标记相吻合,因此就出现一次命中。现在,高速缓存控制器必须从高速缓存行中的数据部分选择正确的字。因为地址中的第2个位被设置了,而CPU想要让高端的字保存在高速缓存行的右边,因此想要的字是0777。

这里要理解的一件重要的事情是,对于高速缓存的标记来说没有必要包含完整的地址,因为根据地址在高速缓存中的位置就能推算出地址的一部分。这一点之所以重要是因为它减少了高速缓存行中的总位数,从而也就降低了高速缓存的成本。一般而言,标记只包括地址中散列算法不使用的位。在这个例子中,“位<5..3>”选择出了可以用来保存数据的唯一的高速缓存行。于是,用来构成高速缓存行索引的位可以从标记中省略。类似地,因为该高速缓存行包含8字节,所以地址中的低3位(<2..0>)也不需要保存在标记中。因此,在标记中只需要保存“位<11..6>”。

举第二个例子,考虑当CPU试图从地址0130读取数据时会发生什么情况。散列算法选择“位<5..3>”用于索引,如图2-13所示,这几位又等于3。

读取高速缓存行3,并且将来自地址的标记01同高速缓存中的标记012进行比较。它们并不匹配,所以就发生了一次缺失。现在必须把地址发送到主存储系统来检索数据。接着,主存储器内从地址0130开始的两个字载入到有索引的高速缓存行中。因为第2位是0,所以需要的是双字中低端的那个字,于是高速缓存行左半部分中的字被返回给CPU。

再举最后一个例子,读取地址06540会造成一次缺失,因为被索引的行(高速缓存行4)没有包含有效的标记,这会自动导致一次缺失。

图2-14总结了前面的几个例子,还给出了其他几个例子。

2.3.3 直接映射高速缓存的缺失处理和替换策略
当出现一次高速缓存缺失的时候,必须从主存储器读取数据然后返回给CPU。数据还要载入高速缓存,以便在近期内再次引用它时可以立即得到。如果高速缓存行比一个字大,就要从主存储器中多读几个字,以便在高速缓存中载入完整的一行。此时就会造成邻接字被预取(prefetched),从而充分利用了局部引用特性。要读取完整的一行,高速缓存可能需要额外的存储器周期,也可能不需要,这取决于主存储系统的设计。为了获得最佳性能,主存储系统应该以高速缓存行的大小为单元传送数据,这称为突发模式(burst mode)的传送。这种模式能让完整的高速缓存一行在一次存储器操作中传送。没有突发模式,传送必须以更小的单元来执行,从而增加了开销。

一旦主存储器提供了所需的数据,就必须在高速缓存中找到一个位置保存它。这个位置必须经过挑选,以便散列算法在以后的查找操作期间能正确地定位高速缓存的行。在直接映射高速缓存中,这个位置是通过采用散列算法计算行中首字的地址,从而得到该行将会保存在高速缓存中什么地方的行索引值。最初检查的和发生缺失时找到的结果始终都是同一行。新行必须保存在这个位置,因为如果把它保存在高速缓存中的其他行,以后引用时就不能通过索引找到它。这是直接映射高速缓存唯一可能采用的替换策略。

如果缺失操作期间被替换的高速缓存行以前并不是有效行,那么就可以将新行的数据载入到这一行中,并且设定标记,指出数据的地址。该行的有效位也设为ON。如果这一行以前保存着不同地址的有效数据,那么这些数据就会被丢弃,并用新行替换它们。如果采用写回高速缓存机制,而且修改了原来的数据(参见2.2.5节),那么在替换它之前必须将它写回存储器,从而不会丢失修改过的数据。

举一个例子,再次考虑2.3.2节里的例子和图2-11描绘的高速缓存。如果CPU试图读取位于00130的字,那么就会发生一次缺失,因为高速缓存内的行3缓存的是从地址01230到地址01237的数据。如果主存储器在地址00130处保存的值为0222,在地址00134处保存的值为0333,那么在处理缺失之后,高速缓存将被更新为图2-15所示的内容。

新标记和新数据替换了原来的标记和数据,高速缓存内其余的行则保持不变。CPU最初请求的数据0222在新行写入高速缓存的同时返回给CPU。

2.3.4 直接映射高速缓存的总结
在直接映射高速缓存中,主存储器内数据的地址和高速缓存内可能保存该地址的位置或行之间具有一一对应的关系。数据在高速缓存内是通过散列该地址来定位的,计算得出了高速缓存中可以保存该数据的唯一一行的索引。直接映射高速缓存既可以使用写直通策略,也可以使用写回策略。

直接映射高速缓存是实现起来最简单的高速缓存,因为在读或者写操作期间只可能有一个搜索命中的位置。这就简化了控制逻辑,从而让实现的成本更低。遗憾的是,这也是直接映射高速缓存的缺点,因为许多不同的地址都被散列到了同一行上。对于一个带有病态引用模式的程序,其中局部引用的数据或者指令地址都产生了相同的索引或者总是位于一小组索引的集合中,这样的程序就无法从高速缓存获益,因为命中率将非常低。在这样的情况下,高速缓存行在被再次命中之前总是被替换掉,所以这种情形称为高速缓存颠簸(cache thrashing)(这个名字来源于虚拟存储调页系统中出现的相同现象)。下面一节介绍对直接映射高速缓存所做的一种改进,以此减少颠簸,提高命中率。

时间: 2024-09-16 20:28:05

《现代体系结构上的UNIX系统:内核程序员的对称多处理和缓存技术(修订版)》——2.3 直接映射高速缓存的相关文章

《Linux/UNIX系统编程手册(上、下册)》——1.3 标准化

1.3 标准化 20世纪80年代末,可用的UNIX实现层出不穷,由此也带来了种种弊端.有些UNIX实现基于BSD,而另一些则基于System V,还有一些则是对两大"流派""兼容并蓄".更有甚者,每个厂商都在自己的UNIX实现中添加了额外特性.其结果是将软件及技术人员在不同UNIX实现间转移就变得异常困难.这一形式有力地推动了C语言和UNIX系统的标准化进程,使得应用程序能够在不同操作系统间很方便地进行移植.接下来,将介绍由此而产生的各种标准. 1.3.1 C编程语

《Linux/UNIX系统编程手册(上、下册)》——1.2 Linux简史

1.2 Linux简史 术语Linux通常用来指代完整的类UNIX(UNIX-like)操作系统,Linux内核只是其中的一部分.这么定义多少有些措辞不当,因为一般商业Linux发布版中所含的诸多关键组件实际上发源于另一项目,早在Linux问世前几年就已经启动了. 1.2.1 GNU项目 1984年,Richard Stallman之前一直供职于MIT的一位天赋异禀的程序员,开始着手创建一个"自由的(free)"UNIX实现.Stallman的观点属于道德层面,而对"free

《Linux/UNIX系统编程手册(上、下册)》——1.4 总结

1.4 总结 1969年,贝尔实验室(AT&T的一个部门)的Ken Thompson在Digital PDP-7小型机上首次实现了UNIX系统.对该操作系统而言,无论是理念还是其双关语的称谓都来源于早期的MULTICS系统.时至1973年,UNIX已经被移植到了PDP-11小型机上,并以C语言对其进行了重写,C编程语言是由贝尔实验室的Dennis Ritchie设计并实现的.因为法律禁止AT&T销售UNIX,于是,在象征性地收取了一定的费用之后,AT&T索性将UNIX系统散布进了大

《Linux/UNIX系统编程手册(上、下册)》——第1章 历史和标准 1.1UNIX和C语言简史

第1章 历史和标准 Linux是UNIX操作系统家族中的一员.就计算机的发展而言,UNIX历史悠久.本章的第一部分会简要介绍UNIX的历史--以对UNIX系统和C编程语言起源的回顾拉开序幕,接着会述及成就今日Linux系统的两大关键因素:GNU项目和Linux内核的开发. UNIX系统最引人关注的特征之一,是其开发不受控于某一厂商或组织.相反,许多团体--既有商业团体,也有非商业团体--都曾为UNIX的演进做出过贡献.这一渊源使UNIX集多种开创性的特性于一身,但同时也带来了负面影响--随着时间

在 Unix 系统上查找数据的最佳工具和技巧

有时候在 Unix 系统上查找信息就如同大海捞针.如果重要的信息被淹没在大量文本中,它们也很难被注意到.目前我们中的很多人都在处理"大数据" -- 从数十亿字节大小的日志文件和巨大的各种格式记录集合中挖掘商业情报. 幸运的是,只有在两种情况下,你才需要在成堆的数据中挖掘,继而完成你的工作 -- 当你知道你要找什么和当你不知道的时候.:) 最佳工具和技巧取决于你面临两种情况中的哪一种. 当你知道的时候 当你知道你要找什么,grep 就是你的朋友,这不只是在你查找特定文本的时候.grep

“鹰”之路—访著名linux内核程序员大鹰

在网络安全界,如果说起艾奇伟,或许大家都会茫然,但是如果说不认识大鹰,那一定会有人取笑你.这个名字确实很响亮,就像鹰一样飞遍了网络安全界.登录他的主页www.e4gle.org,全英文版面的设计可以看出其中的技术含量,尤其是他所在的组织的WSS,已经达到了世界级水平.Linux界的名人在生活中是什么样子呢?带着兴奋与神秘,我来到了约定地点. "你们早来了?"话到人到,大鹰突然出现了. 在茫茫人海中,与他擦肩而过的时候,你会认为这个人是顶级内核程序员吗?Adidas的上衣,一身休闲的打扮

Unix系统常见十大故障详细分析

  SCO Openserver 5.0.5作为一种高效稳定.安全性能高的多用户操作系统,在金融.保险.电信等部门得到广泛的应用.在系统日常维护工作中,有时会遇到一些系统故障.笔者把常见的十个问题总结了一下,希望对大家能有所帮助. 一.打开计算机电源后,主控台屏幕上出现如下信息:boot not found cannot open stage 1 boot failure:error loading hd(40)/boot,然后死机. 分析:这表明系统根目录下的Boot文件丢失或找不到.Boot

实用:使用PHP脚本修改Linux或Unix系统口令

本文介绍如何使用PHP脚本修改Linux或Unix系统口令. 需要的工具和安装: 你必须安装下面的工具和软件: – 修改口令的Shell脚本; – Sudo 访问权; – Apache or Lighttpd web 服务器; – PHP服务端程序. 步骤1: 安装可以修改用户口令的shell脚本 该脚本可以实际用于修改Linux用户的口令(已在Linux和FreeBSD测试). 例子: shell脚本代码 #!/bin/sh # \ exec expect -f "$0″ ${1+"

SCO UNIX系统root密码丢失的处理

在重要的计算机应用领域中,UNIX系统起着主导作用.UNIX具有很强的可伸缩性.健壮性,完全支持Internet和良好的用户界面,是其它非UNIX系统无法做到和替代的.目前,UNIX覆盖了大多数银行.电信.保险.证券.铁路等系统应用,即使在Internet应用方面,使用的也绝大多数是各计算机厂商提供的各种UNIX系统,可以说UNIX无处不在. SCO公司的SCO UNIX系列产品在全球市场份额所占的比重相当大.由于SCO UNIX不依赖 于任何硬件平台,在基于Intel公司的芯片的个人计算机和网