http://www.tyut.edu.cn/kecheng1/2008/site04/courseware/chapter5/5.5.htm
5.5 高速缓冲存储器cache
随着CPU时钟速率的不断提高,当它访问低速存储器时,不得不插入等待周期,这就明显降低了高速CPU的效率。为了与CPU的速率相匹配,可以采用高速存储器,但它的成本很高,用来组成大容量的主存储器很不经济。成本较低的存储器适宜制作大容量的主存储器,但是速度过低。为了折中地解决速率与成本两者之间的矛盾,兼顾高速与低成本各自的优势,在现代微机系统中,采用了高速缓冲存储器cache技术。cache通常采用与CPU同样的半导体材料制成,速度一般比主存高5倍左右。由于其高速而高价,故容量通常较小,一般为几KB到几十KB,仅用来保存主存中最经常用到的一部分内容的副本。
统计表明,利用一级cache,可使存储器的存取速度提高4~10倍。当速度差更大时,可采用多级cache。目前大多数PC的高速缓存都分为两个级别:L1 cache和L2 cache。L1 cache集成在CPU芯片内,时钟周期与CPU相同;L2 cache通常封装在CPU芯片之外,采用SRAM芯片,时钟周期比CPU慢一半或更低。就容量而言,L2 cache的容量通常比L1 cache大一个数量级以上,从几百KB到几千KB不等。80486CPU芯片内有8KB的cache,存放程序和数据,并支持L2 cache。cache在微机系统中的位置如图5.24所示。
5.5.1 cache的工作原理
在CPU的所有操作中,访问内存是最频繁的操作。由于一般微机中的主存储器主要由MOS型动态RAM构成,其工作速度比CPU低一个数量级,加上CPU的所有访问都要通过总线这个瓶颈,所以,缩短存储器的访问时间是提高计算机速度的关键。采用在CPU和内存之间加进高速缓冲存储器cache的办法较好地解决了这一问题。所谓“cache”原意是指勘探人员的藏物处,这里引申为“高速缓存”。在保证系统性能价格比的前提下,使用速度与CPU相当的SRAM芯片组成小容量的高速缓存器,使用低价格、小体积能提供更大存储空间的DRAM芯片(或内存条)组成主存储器。
下面,以取指为例对cache的工作原理进行说明。命中率是高速缓存子系统操作有效性的一种测度,它被定义为高速缓存命中次数与存储器访问总次数之比,用百分率来表示,即
例如,若高速缓存的命中率为92%,则意味着CPU可用92%的总线周期从高速缓存中读取数据。换句话说,仅有8%的存储器访问是对主存储器子系统进行的。假设经过前面的操作cache中已保存了一个指令序列,当CPU按地址再次取指时,cache控制器会先分析地址,看其是否已在cache中,若在,则立即取来,否则,再去访问内存。因为大多数程序有一个共同特点,即在第一次访问了某个存储区域后,还要重复访问这个区域。CPU第一次访问低速DRAM时,要插入等待周期。当CPU进行第一次访问时,也把数据存到高速缓存区。因此,当CPU再访问这一区域时,CPU就可以直接访问高速缓存区,而不访问低速主存储器。因为高速缓存器容量远小于低速大容量主存储器,所以它不可能包含后者的所有信息。当高速缓存区内容已装满时,需要存储新的低速主存储器位置上的内容,以代替旧位置上的内容。高速缓存器的设计目标是使CPU访问尽可能在高速缓存器中进行,其工作原理如图5.25所示。
5.5.2 cache的读/写策略
这里讲述的读/写策略依然是针对CPU对存储器的读/写访问的,即cache读操作实际上是CPU读存储器,cache写操作实际上是CPU写存储器。在cache中应尽量存放CPU最近一直在使用的数据。当cache装满后,可将长期不用的数据删除,以提高cache的使用效率。为保持cache中的数据与主存储器中的数据的一致性,同时避免CPU在读/写过程中遗失新数据,确保cache中更新过的数据不会因覆盖而消失,必须将cache中的数据及时更新并准确地反映到主存储器。这里涉及CPU、cache与主存储器三者之间的协调,使得读/写操作复杂化,从而也引入了一些新的方法与专业术语。
1.读策略
读策略又可分为以下两种。
(1)贯穿读出式(look through)
贯穿读出式的原理如图5.26所示。
在这种方式下,cache位于CPU与主存之间,CPU对主存的所有数据请求都首先送到cache,由cache在自身查找。如果命中,则切断CPU对主存的请求,并将数据送出;如果未命中,则将数据请求传给主存。该方法的优点是降低了CPU对主存的请求次数,缺点是延迟了CPU对主存的访问时间。
(2)旁路读出式(look aside)
旁路读出式的原理如图5.27所示。
在这种方式中,CPU发出数据请求,并不是单通道地穿过cache,而是向cache和主存同时发出请求。由于cache速度更快,如果命中,则cache在将数据回送给CPU的同时,还来得及中断CPU对主存的请求;若未命中,则cache不做任何动作,由CPU直接访问主存。它的优点是没有时间延迟,缺点是每次CPU都要访问主存,这样就占用了部分总线时间。还有另外一个值得注意的概念,那就是“行填充”。
每当CPU由主存储器读入数据时,同时还要将该数据复制到cache中。即使当前CPU仅读一个字节,cache控制器也总是要将主存储器中包含该字节的一个完整的cache行复制到cache中。一个cache行占据32个字节,这种从主存储器向cache传送一行数据的操作就称为cache行填充(line fill),这是考虑到数据与代码往往具有连续性,CPU下一次所需的数据或代码通常还是处在上一次的数据或代码附近,即同处一cache行中的可能性最大,这样处理就提高了cache的命中率。由此可见,cache中的数据是由空到满逐行建立起来的。
cache行的容量过小时,会产生频繁的行填充操作,整机效率不会有明显提高。然而当一行的容量空间过大时,填充一行所需的时间较长,而且可能有许多数据并不是CPU最近所需要的,从而造成过大的浪费,这样也不会使整机效率显著上升。因此必须折中各种因素来选择cache行的大小。Intel体系的32位微处理器中一个cache行由32个字节组成,并且cache控制器用后面即将讨论的突发模式传送数据块,一次突发传送一个cache行的32个字节,其传输速率比常规访问几乎高出一倍。
就cache整体容量而言,也有同cache行类似的矛盾。cache容量过小将起不到明显改善系统性能的效果;增加cache容量自然可以存储更多的信息,但随之也就增加了成本,且搜索大容量的cache还不如搜索小容量的速度快。可见,cache的容量要兼顾多种因素,选择适中,一般取主存储器的几十分之一或者几百分之一。
每当CPU所需的数据或代码不在cache中而出现未命中时,cache控制器就必须在主存储器中读取数据,这段时间较长且需要等待。此时,cache控制器使“准备好”或类似的信号变为无效,于是CPU插入等待时钟周期,cache控制器将访问主存储器,将所需数据传送给CPU。
2.替换策略
当cache已经装满后,主存储器中新的数据还要不断地替换掉cache中过时的数据,这就产生了cache块数据的替换策略。那么应替换哪些cache块才能提高命中率呢?理想的替换策略应该使得cache中总是保存着最近将要使用的数据,不用的数据则被替换掉,这样才能保证很高的命中率。目前,使用较多的是随机(random)替换、先入先出(FIFO)替换与近期最少使用(LRU)替换三种策略。
(1)随机(random)替换策略
随机替换是不顾cache块过去、现在及将来使用的情况而随机地选择某块进行替换,这是一种最简单的方法。
(2)先入先出(FIFO)替换策略
先入先出(FIFO)替换策略的基本思想是:根据进入cache的先后次序来替换,先调入的cache块被首先替换掉。这种策略不需要随时记录各个块的使用情况,容易实现,且系统开销小。其缺点是一些需要经常使用的程序块可能会被调入的新块替换掉。
(3)近期最少使用(LRU)替换策略
近期最少使用LRU(least recently used)替换策略的基本思想是:把CPU近期最少使用的块作为被替换的块。这种替换算法相对合理,命中率最高,是目前最常采用的方法。它需要随时记录cache中各块的使用情况,以便确定哪个块是近期最少使用的块,实现起来比较复杂,系统开销较大。不管是哪种策略,它们都只能用硬件电路来实现,试想若依靠软件的话,cache的“高速”含义就毫无意义了。
3.写策略
以上讨论的都是CPU读数据的情况,另一种是CPU写数据的情况。cache控制器同样会判断其地址是否定位在cache中。如果在,CPU的数据就会写到cache中。对于进一步的主存储器操作,cache控制器有以下几种主要的写策略。
(1)通写方式(write through)
通写方式的原理如图5.28所示。任一从CPU发出的写信号送到cache的同时,也送到主存,以保证主存的数据能同步地更新。它的优点是操作简单,但由于主存的速度相对较慢,降低了系统的写速度并占用了部分总线时间。
(2)回写方式(write back)
回写方式的原理如图5.29所示。
为了尽量减少对主存的访问次数,克服通写方式中每次数据写入都要访问主存,从而导致系统写速度降低并占用总线时间的弊病,又有了回写方式。它的工作原理是:数据一般只写到cache,而不写入主存,从而使写入的速度加快。但这样有可能出现cache中的数据得到更新而对应主存中的数据却没有变(即数据不同步)的情况。此时可在cache中设置一个标志地址及数据陈旧的信息,只有当cache中的数据被再次更改时,才将原更新的数据写入主存相应的单元中,然后接受再次更新的数据。这样就保证了cache和主存中的数据不产生冲突。
(3)失效(invalidation)
当系统中存在其他微处理器或DMA操作的系统部件时,主存储器即成为共享存储器。它们之中的任何一方对主存储器都有可能覆盖写入。此时cache控制器必须通报有关的cache行,它们的数据由于主存储器已被修改而成为无效,这种操作就称为cache失效。
5.5.3 cache的地址映射
为了把数据从主存中取出送入cache中,必须使用某种地址转换机制把主存地址映射到cache中定位,称为地址映射。实现方法是:将主存和cache都分为大小相等的若干块(或称页),每块的大小为2n个字节,通常为29(512B),210(1 024B)或211(2 048B)等,以块为单位进行映射。假设某系统的cache容量为1MB,若每块容量为1KB,则被分为1 024块;cache容量为8KB,每块容量也是1KB,则被分为8块。下面以此为例,介绍三种cache的地址映射方法。
1.直接地址映射(direct mapping)
直接地址映射是指主存中每一个块只能映射到某一固定的cache中,如图5.30所示。
把主存按cache大小分为若干组,每一组按对应的块号进行映射。如主存的第0块、第8块、…、第1 016块,只能映射到cache的第0块;而主存的第1块、第9块、…、第1 017块只能映射到cache的第1块,依次类推。
这种映射方法比较简单,且地址转换速度快,但不够灵活,使得cache的存储空间得不到充分利用。
2.全相联地址映射(fully associative mapping)
全相联地址映射是指主存中的每一块都可以映射到cache的任何一块位置上,如图5.31所示。这种映射方法比较灵活,cache的利用率高,但地址转换速度慢,且需要采用某种置换算法将cache中的内容调入调出,实现起来系统开销大。
3.组相联地址映射(set associative mapping)
组相联地址映射是直接地址映射和全相联地址映射的折中方案,如图5.32所示。主存和cache都分组,主存中一个组内的块数与cache中的分组数相同。组间采用直接地址映射,而组内采用全相联地址映射。主存中的各块与cache的组号间有固定的映射关系,但可以自由映射到对应的cache组中的任何一块。如主存中的第0块可映射到cache的第0组的第0块或第1块;主存中的第1块可映射到cache的第1组的第2块或第3块等。这种映射方法比直接地址映射灵活,比全相联地址映射速度快。实际上,若组的大小为1时,就变成了直接映射;若组的大小为整个cache的尺寸时,就变成了全相联映射。
http://blog.csdn.net/efan_linux/article/details/4584807
Cache 的write back和write through
为了保证cache和memory的数据一致性,通常有三种方法:
1〉write through:CPU向cache写入数据时,同时向memory也写一份,使cache和memory的数据保持一致。优点是简单,缺点是每次都要访问memory,速度比较慢。
2〉post write:CPU更新cache数据时,把更新的数据写入到一个更新缓冲器,在合适的时候才对memory进行更新。这样可以提高cache访问速度,但是,在数据连续被更新两次以上的时候,缓冲区将不够使用,被迫同时更新memory。
3〉write back:CPU更新cache时,只是把更新的cache区标记一下,并不同步更新memory。只是在cache区要被新进入的数据取代时,才更新memory。这样做的原因是考虑到很多时候cache存入的是中间结果,没有必要同步更新memory。优点是CPU执行的效率提高,缺点是实现起来技术比较复杂。
回写与透写 | |
|
1.Cache的两个类型
--Write Through
当写数据进Cache时,也同时更新了相应的Memory里的内容
--Write back
只是写到Cache里,Memory的内容要等到cache保存的要被别的数据替换或者系统做cache flush时,才会被更新。
2.Cache的两个函数
--Flush
把Cache内容写回Memory,当Cache为Write through,不需要Flush
--Invalidate
把Cache内容直接丢掉不要。
3.Cache的使用场合
当有DMA在使用memory的时候,一般要用到cache的处理。因为DMA在访问memory时是不经过cache的。比较典型的比如在Ethernet,wireless,USB等driver里,DMA会操作descriptors和packet buffers,Driver要做这些处理
--如果driver使用descripter和packet buffer的地址都是cache的地址,那么
a).Driver在读descripter里一些状态比如Owned by CPU/DMA,有没有收到包时,要对descripter当前结构里的内容做cache invalidate,收到packet后,也要对packet buffer做cache invalidate
b).Driver在写descripter里一些状态比如Owned by DMA,要发送包时,要对descripter当前结构里的内容做cache flush,发送packet时,也要对packet buffer做cache flush
--有些driver会对descripter使用uncache 地址,那么上面两种情况里invalidate/flush就不用做了。一般很少会对packet buffer也用uncache地址的,因为对packet内容的处理将会很频繁,使用uncache会很慢。而descripter一般由于结构比较小,如果也使用cache地址的话,做invalidate/flush的时间消耗可能会比uncache的还要多。