三千红尘路,寥寥九州土
长安于我意何如
无关青云路,无关诗书
无你处,无江湖
——少司命《烟笼长安》
利用局部性原理组织存储器层次结构。
块(或行)是两级层次结构中信息交换的最小单元。
主存地址分割: 【标识】【索引】【块内位移】 【标识】【索引】【块内位移】 【标识】【索引】【块内位移】
其中,标识和索引都属于块地址。
(低位)索引:选定对应的块(直接映射)或组(组相联),全相联没有索引。
(高位)标识:判断所选块,地址是否匹配。
块内位移:块大小为2mB→m位块内位移。若存储器按字节编址,最低位还有2位字节偏移。
不同相联度下,Cache的结构图
Cache的容量与索引(index),相联度,块大小的关系
C a c h e 的容量 = 2 i n d e x × 相联度 × 块大小 Cache的容量=2^{index}\times 相联度\times 块大小 Cache的容量=2index×相联度×块大小
平均访问时间 = 命中时间 + 不命中率 × 不命中开销 平均访问时间=命中时间+不命中率\times 不命中开销 平均访问时间=命中时间+不命中率×不命中开销
C P U 时间 = ( C P U 执行周期数 + 存储器停顿周期数 ) × 时钟周期时间 CPU时间=(CPU执行周期数+存储器停顿周期数)\times 时钟周期时间 CPU时间=(CPU执行周期数+存储器停顿周期数)×时钟周期时间
存储器停顿时钟周期数 = “读”的次数 × 读不命中率 × 读不命中开销 + “写”的次数 × 写不命中率 × 写不命中开销 = 访存次数 × 不命中率 × 不命中开销 \begin{aligned} 存储器停顿时钟周期数 & = “读”的次数\times 读不命中率\times 读不命中开销+“写”的次数\times 写不命中率\times 写不命中开销\\ & = 访存次数\times 不命中率\times 不命中开销 \end{aligned} 存储器停顿时钟周期数=“读”的次数×读不命中率×读不命中开销+“写”的次数×写不命中率×写不命中开销=访存次数×不命中率×不命中开销
C P U 时间 = ( C P U 执行周期数 + 访存次数 × 不命中率 × 不命中开销 ) × 时钟周期时间 CPU时间=(CPU执行周期数+访存次数\times 不命中率\times 不命中开销)\times 时钟周期时间 CPU时间=(CPU执行周期数+访存次数×不命中率×不命中开销)×时钟周期时间
C P U 时间 = I C × ( C P I e x e c u t i o n + 访存次数 I C × 不命中率 × 不命中开销 ) × 时钟周期时间 = I C × ( C P I e x e c u t i o n + 每条指令的平均访存次数 × 不命中率 × 不命中开销 ) × 时钟周期时间 \begin{aligned} CPU时间 & = IC\times (CPI_{execution}+\frac{访存次数}{IC}\times 不命中率\times 不命中开销 )\times 时钟周期时间\\ &=IC\times (CPI_{execution}+每条指令的平均访存次数\times 不命中率\times 不命中开销 )\times 时钟周期时间 \end{aligned} CPU时间=IC×(CPIexecution+IC访存次数×不命中率×不命中开销)×时钟周期时间=IC×(CPIexecution+每条指令的平均访存次数×不命中率×不命中开销)×时钟周期时间
Cache不命中对于一个CPI较小而时钟频率较高的CPU来说,影响是双重的:
因此,Cache对于低CPI、高时钟频率的CPU来说更加重要。
三个方面:
强制性不命中(Compulsory miss)
当第一次访问一个块时,该块不在Cache中,需从下一级存储器中调入Cache,这就是强制性不命中。 (冷启动不命中,首次访问不命中)
容量不命中(Capacity miss )
如果程序执行时所需的块不能全部调入Cache中,则当某些块被替换后,若又重新被访问,就会发生不命中。这种不命中称为容量不命中。
冲突不命中(Conflict miss)
在组相联或直接映像Cache中,若太多的块映像到同一组(块)中,则会出现该组中某个块被别的块替换(即使别的组或块有空闲位置),然后又被重新访问的情况。这就是发生了冲突不命中。
(碰撞不命中,干扰不命中)
观察到:
相联度越高,冲突不命中就越少;
强制性不命中和容量不命中不受相联度的影响;
强制性不命中不受Cache容量的影响,但容量不命中却随着容量的增加而减少。
为什么不命中率先下降后上升?
此外,增加块大小还会增加不命中开销。
针对容量不命中最直接的方法,缺点是增加成本,也可能增加命中时间。
这种方法多用于片外Cache。
伪相联优点:命中时间小,不命中率低。这是结合了直接映像和组相联的优点。
基本思想及工作原理
缺点 :多种命中时间 – 快速命中与慢速命中。要保证绝大多数命中都是快速命中。
多种命中时间使CPU流水线设计复杂化,往往应用在离处理器较远的Cache上,如第二级Cache。
主要思想:在编译时加入预取指令,在数据被用到之前发出预取请求。
按照预取数据所放的位置,可把预取分为两种类型:
按照预取的处理方式不同,可把预取分为:
在预取数据的同时,处理器应能继续执行。只有这样,预取才有意义。这称为非阻塞Cache或非锁定Cache。
编译器控制预取的目的:使执行指令和读取数据能重叠执行。
循环是预取优化的主要对象
每次预取需要花费一条指令的开销,保证这种开销不超过预取所带来的收益。
编译器可以通过把重点放在那些可能会导致不命中的访问上,使程序避免不必要的预取,从而较大程度地减少平均访存时间。
基本思想:通过对软件进行优化来降低不命中率。
(特色:无需对硬件做任何改动)
将本来相互独立的多个数组合并成为一个复合数组,以提高访问它们的局部性。
将若干个独立的循环融合为单个的循环。这些循环访问同样的数组,对相同的数据作不同的运算。这样能使得读入Cache的数据在被替换出去之前,能得到反复的使用 。
把对数组的整行或整列访问改为按块进行,尽量集中访问,减少替换,提高访问的局部性。例:
在Cache和它从下一级存储器调数据的通路之间设置一个全相联的小Cache,称为“牺牲”Cache(Victim Cache)。用于存放被替换出去的块(称为牺牲者),以备重用。
“牺牲”Cache是一种能减少冲突不命中次数而又不影响时钟频率的方法。
作用:对于减小冲突不命中很有效,特别是对于小容量的直接映像数据Cache,作用尤其明显。例如项数为4的Victim Cache,能使4KB Cache的冲突不命中减少20%~90%。
第一级Cache(L1)小而快
第二级Cache(L2)容量大
性能分析:
平均访存时间 = 命中时 间 L 1 + 不命中 率 L 1 × 不命中开 销 L 1 平均访存时间=命中时间_{L1}+不命中率_{L1}\times 不命中开销_{L1} 平均访存时间=命中时间L1+不命中率L1×不命中开销L1
不命中开 销 L 1 = 命中时 间 L 2 + 不命中 率 L 2 × 不命中开 销 L 2 不命中开销_{L1}=命中时间_{L2}+不命中率_{L2}\times 不命中开销_{L2} 不命中开销L1=命中时间L2+不命中率L2×不命中开销L2
平均访存时间 = 命中时 间 L 1 + 不命中 率 L 1 × ( 命中时 间 L 2 + 不命中 率 L 2 × 不命中开 销 L 2 ) 平均访存时间=命中时间_{L1}+不命中率_{L1}\times (命中时间_{L2}+不命中率_{L2}\times 不命中开销_{L2}) 平均访存时间=命中时间L1+不命中率L1×(命中时间L2+不命中率L2×不命中开销L2)
局部不命中率和全局不命中率
局部不命中率 = 该级 C a c h e 的不命中次数 到达该级 C a c h e 的访问次数 局部不命中率=\frac{该级Cache的不命中次数}{到达该级Cache的访问次数} 局部不命中率=到达该级Cache的访问次数该级Cache的不命中次数
例如上面式子中的不命中率L2就是局部不命中率。
全局不命中率 = 该级 C a c h e 的不命中次数 C P U 发出的访存的总次数 全局不命中率=\frac{该级Cache的不命中次数}{CPU发出的访存的总次数} 全局不命中率=CPU发出的访存的总次数该级Cache的不命中次数
全局不命中 率 L 2 = (局部)不命中 率 L 1 × (局部)不命中 率 L 2 全局不命中率_{L2}=(局部)不命中率_{L1}\times (局部)不命中率_{L2} 全局不命中率L2=(局部)不命中率L1×(局部)不命中率L2
全局不命中率比局部不命中率更有意义,它指出了在CPU发出的访存中,究竟有多大比例是穿过各级Cache,最终到达存储器的。
采用两级Cache时,每条指令的平均访存停顿时间:
每条指令的平均访存停顿时间 = 每条指令的平均不命中次 数 L 1 × 命中时 间 L 2 + 每条指令的平均不命中次 数 L 2 × 不命中开 销 L 2 每条指令的平均访存停顿时间=每条指令的平均不命中次数_{L1}\times 命中时间_{L2}+每条指令的平均不命中次数_{L2}\times 不命中开销_{L2} 每条指令的平均访存停顿时间=每条指令的平均不命中次数L1×命中时间L2+每条指令的平均不命中次数L2×不命中开销L2
注意:停顿时间中不包含命中时间L1。
块大小
需要考虑的另一个问题:多级包容性(便于实现一致性检测)
Cache中的写缓冲器导致对存储器访问的复杂化:在读不命中时,所读单元的最新值有可能还在写缓冲器中,尚未写入主存。
解决问题的方法(读不命中的处理)
在写回法Cache中,也可采用写缓冲器。
注意到,未采用写缓冲合并时,4次写入内容就填满了缓冲区,每个项目的四分之三被浪费。在为采用缓冲区时,图示上半部分右侧的字只会用于同时写多个字的指令。
也就是说,一个项(块)有多个字,一次写入操作很有可能只写入一个字,而项中的其余的字就可以用于提供合并。
命中时间直接影响到处理器时钟频率的高低。在当今的许多计算机中,往往是Cache的访问时间限制了处理器的时钟频率。
物理Cache
虚拟索引+物理标识
为什么之前情况下的Cache大小不受此限制?因为之前的物理Cache,索引位由Cache容量决定,剩余位均归于标识位。
对第一级Cache的访问按流水方式组织:访问Cache需要多个时钟周期才可以完成。
实际上并不能真正减少Cache的命中时间,但可以提高访问Cache的带宽。
开发指令级并行性所遇到的一个挑战是:当要每个时钟周期流出超过4条指令时,要提供足够多条彼此互不相关的指令是很困难的。
一个解决方法:采用踪迹 Cache
存放CPU所执行的动态指令序列,包含了由分支预测展开的指令,该分支预测是否正确需要在取到该指令时进行确认。
缺点
地址映像机制复杂;
相同的指令序列有可能被当作条件分支的不同选择而重复存放;
优点
能够提高指令Cache的空间利用率。
“+”号:表示改进了相应指标。
“-”号:表示它使该指标变差。
空格栏:表示它对该指标无影响。
复杂性:0表示最容易,3表示最复杂。
优化技术 | 不命中率 | 不命中开销 | 命中时间 | 硬件复杂度 | 说明 |
---|---|---|---|---|---|
增加块大小 | + | - | 0 | 实现容易;Pentium 4 的第二级Cache采用了128字节的块 | |
增加Cache容量 | + | - | 1 | 被广泛采用,特别是第二级Cache | |
提高相联度 | + | 1 | 被广泛采用 | ||
牺牲Cache | + | 2 | AMD Athlon采用了8个项的Victim Cache | ||
伪相联Cache | + | 2 | MIPS R10000的第二级Cache采用 | ||
硬件预取指令和数据 | + | 2~3 | 许多机器预取指令,UltraSPARC Ⅲ预取数据 | ||
编译器控制的预取 | + | 2~3 | 需同时采用非阻塞Cache;有几种微处理器提供了对这种预取的支持 | ||
用编译技术减少Cache不命中次数 | + | 0 | 向软件提出了新要求;有些机器提供了编译器选项 | ||
使读不命中优先于写 | + | - | 1 | 在单处理机上实现容易,被广泛采用 | |
写缓冲合并 | + | 1 | 与写直达合用,广泛应用,例如21164,UltraSPARC Ⅲ | ||
尽早重启动与关键字优先 | + | 2 | 被广泛采用 | ||
非阻塞Cache | + | 3 | 在支持乱序执行的CPU中使用 | ||
两级Cache | + | 2 | 硬件代价大;两级Cache的块大小不同时实现困难;被广泛采用 | ||
容量小且结构简单的Cache | - | + | 0 | 实现容易,被广泛采用 | |
对Cache进行索引时不必进行地址变换 | + | 2 | 对于小容量Cache来说实现容易,已被Alpha 21164和UltraSPARC Ⅲ采用 | ||
流水化Cache访问 | + | 1 | 被广泛采用 | ||
踪迹Cache | + | 3 | Pentium 4 采用 |
1.增加cache容量的方法多用于( )。
2.提高相联度是以( )为代价。
3.硬件预取可以预取( )和( ),预取内容既可放入( ),也可放入( )中。指令预取通常由( )的硬件完成。
4.按照预取数据所放的位置,可把预取分为( )和( );按照预取的处理方式不同,可把预取分为( )和( )。
5.cache访问流水化是对第( )级cache按流水化方式组织。
1.片外Cache
2.增加命中时间
3.指令 数据 Cache 外缓冲器 Cache之外
4.寄存器预取 Cache预取 故障性预取 非故障性预取
5.一
- CPIexecution越低,固定周期数的Cache不命中开销的相对影响就越大。
- 在计算CPI时,不命中开销的单位是时钟周期数。因此,即使两台计算机的存储层次完全相同,时钟频率较高的CPU的不命中开销较大,其CPI中存储器停顿这部分也较大。
降低不命中率,减少不命中开销,减少命中时间。
- 强制不命中:当第一次访问一个块时,该块不在Cache中,需从下一级存储器中调入Cache,这就是强制不命中。
- 容量不命中:如果程序执行时所需的块不能全部调入Cache中,则当某些块被替换后,若又重新被访问,就会发生不命中。这种不命中称为容量不命中。
- 冲突不命中:在组相联或直接映像Cache中,若太多的块映像到同一组(块)中,则会出现该组中某个块被别的块替换(即使别的组或块有空闲位置),然后又被重新访问的情况。这就是发生了冲突不命中。
相联度会影响冲突不命中。
容量会影响容量不命中。
增加块大小;增加Cache容量;提高相联度;伪相联Cache;硬件预取;编译器预取;编译器优化;牺牲Cache。
两级Cache;让读不命中优先于写;写缓冲合并;请求字处理技术;非阻塞Cache技术。
使用小容量、结构简单的Cache;虚拟Cache;Cache访问流水化;踪迹Cache。
- 一方面它减少了强制性不命中;(不命中率总体下降)
- 另一方面,由于增加块大小会减少Cache中块的数目,所以有可能会增加冲突不命中。(不命中率又升高了)
容量为N的直接映像Cache的不命中率和容量为N/2的两路组相联Cache的不命中率差不多相同。
命中时间小,不命中率低。
- 在逻辑上吧直接映像Cache的空间上下平分为两个区。
- 对于任何一次访问,伪相联Cache先按直接映像Cache的方式去处理。
- 若命中,则其访问过程与直接映像Cache的情况一样。
- 若不命中,则再到另一区相应的位置去查找。
- 若找到,则发生了伪命中。
- 否则就只好访问下一级存储器。
预取应利用存储器的空闲带宽,不能影响对正常不命中的处理,否则可能会降低性能。
使执行指令和读取数据能重叠执行。
数组合并;内外循环交换;循环融合;分块。
使大概率执行的代码放在分支失败处:
- 将转移目标处的基本块和紧跟着该分支指令后的基本块进行对调;
- 把该分支指令换为操作语义相反的分支指令。
将本来相互独立的多个数组合并称为一个复合数组,以提高访问他们的局部性。
将若干个独立的循环融合为单个的循环。这些循环访问同样的数组,对相同的数据作不同的运算。这样能使得读入Cache的数据在被替换出去之前,能得到反复的使用。
在Cache和它从下一级存储器调数据的通路之间设置一个全相联的小Cache,称为“牺牲”Cache。用于存放被替换出去的块,以备重用。
第一级Cache中的数据是否总是同时出现在第二级Cache中。如果是,就说第二级Cache是具有多级包容性的。
- 推迟对读不命中的处理,直到写缓冲器清空。
- 检查写缓冲器的内容,若无相同,继续处理读不命中。
- 如果写缓冲器为空,就把数据和相应地址写入该缓冲器。从CPU的角度来看,该写操作就算是完成了。
- 如果写缓冲器中已有待写入的数据,就要把这次的写入地址与写缓冲器中已有的所有地址进行比较,看是否有匹配的项。如果有地址匹配而对应的位置又是空闲的,就把这次要写入的数据与该项合并。
- 如果写缓冲器满且又没有能进行写合并的项,就必须等待。
- 连续写入多个字的速度快于每次只写入一个字的操作;
- 提高了写缓冲器的空间利用率,减少因写缓冲器满而要进行的等待时间。
- 尽早重启动:请求字没有到达时,CPU处于等待状态。一旦请求字到达,立即送给CPU,让等待的CPU尽早重启动,继续执行。
- 请求字优先:调块时,让存储器首先提供CPU所要的请求字。请求字一旦到达,就立即送给CPU,让CPU继续执行,同时从存储器调入该块的其余部分。
- 在以下情况下,用不用差别不大:
- Cache块较小
- 下一条指令正好访问同一Cache的另一部分
由于新进程的虚拟地址和原进程可能相同,因此每当进行进程切换的时候需要清空Cache。
如果使用进程标识符字段(PID)区分进程,当PID重用时,也需要清空Cache。
- 优点:兼得虚拟Cache和物理Cache的好处。
- 缺点:Cache容量收到限制。
- 优点:
- 能够提高指令Cache的空间利用率。
- 缺点:
- 地址映像机制复杂。
- 相同的指令序列有可能被当作条件分支的不同选择而重复存放。
C P U 时 间 有 C a c h e = I C × ( C P I e x e c u t i o n + 每条指令的平均访存次数 × 不命中率 × 不命中开销 ) × 时钟周期时间 = I C × ( 2 + 1.33 × 2 % × 50 ) × 时钟周期时间 = 3.33 × I C × 时钟周期时间 \begin{aligned} CPU时间_{有Cache} & = IC\times (CPI_{execution}+每条指令的平均访存次数\times 不命中率\times 不命中开销)\times 时钟周期时间\\ & = IC\times (2+1.33\times 2\%\times 50)\times 时钟周期时间\\ & = 3.33\times IC\times 时钟周期时间 \end{aligned} CPU时间有Cache=IC×(CPIexecution+每条指令的平均访存次数×不命中率×不命中开销)×时钟周期时间=IC×(2+1.33×2%×50)×时钟周期时间=3.33×IC×时钟周期时间
注意到,
每条指令的平均时钟周期数 ( C P I ) = C P U 时间 ÷ 指令数( I C ) ÷ 时钟周期时间 每条指令的平均时钟周期数(CPI)=CPU时间{\div} 指令数(IC){\div} 时钟周期时间 每条指令的平均时钟周期数(CPI)=CPU时间÷指令数(IC)÷时钟周期时间
CPI有Cache=3.33
3.33/2.0=1.67(倍)
故有Cache情况下,CPU时间为原来的1.67倍。
C P I 无 C a c h e = 2 + 1.33 × 50 = 68.5 CPI_{无Cache}=2+1.33\times 50=68.5 CPI无Cache=2+1.33×50=68.5
68.5/2.0=34.25(倍)
故无Cache情况下,CPU时间为原来的34.25倍。
平均访存时 间 直接映像 C a c h e = 命中时间 + 不命中 率 直接映像 C a c h e × 不命中开销 = 1 × 2 + 1.4 % × 70 = 2.98 n s \begin{aligned} 平均访存时间_{直接映像Cache} & = 命中时间+不命中率_{直接映像Cache}\times 不命中开销\\ &=1\times 2+1.4\%\times 70\\ &=2.98ns \end{aligned} 平均访存时间直接映像Cache=命中时间+不命中率直接映像Cache×不命中开销=1×2+1.4%×70=2.98ns
平均访存时 间 2 路组相联 C a c h e = 命中时间 + 不命中 率 2 路组相联 C a c h e × 不命中开销 = 1.1 × 2 + 1 % × 70 = 2.9 n s \begin{aligned} 平均访存时间_{2路组相联Cache} & = 命中时间+不命中率_{2路组相联Cache}\times 不命中开销\\ &=1.1\times 2+1\%\times 70\\ &=2.9ns \end{aligned} 平均访存时间2路组相联Cache=命中时间+不命中率2路组相联Cache×不命中开销=1.1×2+1%×70=2.9ns
2路组相联的平均访存时间较低。
C P U 时 间 直接映像 C a c h e = I C × ( C P I e x e c u t i o n + 每条指令的平均访存次数 × 不命中 率 直接映像 C a c h e × 不命中开销 ) × 时钟周期时 间 直接映像 C a c h e = I C × ( 2 + 1.3 × 1.4 % × 35 ) × 2 = 5.274 × I C \begin{aligned} CPU时间_{直接映像Cache}&=IC\times (CPI_{execution}+每条指令的平均访存次数\times 不命中率_{直接映像Cache}\times 不命中开销)\times 时钟周期时间_{直接映像Cache}\\ &=IC\times (2+ 1.3\times 1.4\%\times 35)\times 2\\ &=5.274\times IC \end{aligned} CPU时间直接映像Cache=IC×(CPIexecution+每条指令的平均访存次数×不命中率直接映像Cache×不命中开销)×时钟周期时间直接映像Cache=IC×(2+1.3×1.4%×35)×2=5.274×IC
2路组相联Cache的不命中开销为70ns/2.2=32个时钟周期时间。
C P U 时 间 2 路组相联 C a c h e = I C × ( C P I e x e c u t i o n + 每条指令的平均访存次数 × 不命中 率 2 路组相联 C a c h e × 不命中开销 ) × 时钟周期时 间 2 路组相联 C a c h e = I C × ( 2 + 1.3 × 1 % × 32 ) × 2.2 = 5.315 × I C \begin{aligned} CPU时间_{2路组相联Cache}&=IC\times (CPI_{execution}+每条指令的平均访存次数\times 不命中率_{2路组相联Cache}\times 不命中开销)\times 时钟周期时间_{2路组相联Cache}\\ &=IC\times (2+ 1.3\times 1\%\times 32)\times 2.2\\ &=5.315\times IC \end{aligned} CPU时间2路组相联Cache=IC×(CPIexecution+每条指令的平均访存次数×不命中率2路组相联Cache×不命中开销)×时钟周期时间2路组相联Cache=IC×(2+1.3×1%×32)×2.2=5.315×IC
直接映像Cache的平均性能较好。
失效率 | 失效开销(周期) | 平均访存时间(周期) | |
---|---|---|---|
1路 | 0.098 | 50 | 5.90 |
2路 | 0.076 | 50 | 4.90 |
Cache容量为128KB时,1路、2路失效率分别为:0.010,0.007
失效率 | 失效开销(周期) | 平均访存时间(周期) | |
---|---|---|---|
1路 | 0.010 | 50 | 1.50 |
2路 | 0.007 | 50 | 1.45 |
应该是可以默认直接命中时间为1个时钟周期。
根据伪相联Cache的工作原理分析:
平均访存时 间 伪相联 = 命中时 间 伪相联 + 失效 率 伪相联 × 失效开 销 伪相联 失效 率 伪相联 = 失效 率 2 路 失效开 销 伪相联 = 失效开 销 1 路 伪命中率 = 命中 率 2 路 − 命中 率 1 路 = ( 1 − 失效 率 2 路 ) − ( 1 − 失效 率 1 路 ) = 失效 率 1 路 − 失效 率 2 路 \begin{aligned} 平均访存时间_{伪相联} & = 命中时间_{伪相联}+失效率_{伪相联}\times 失效开销_{伪相联}\\ 失效率_{伪相联}&=失效率_{2路}\\ 失效开销_{伪相联}&=失效开销_{1路}\\ 伪命中率&=命中率_{2路}-命中率_{1路}\\ &=(1-失效率_{2路})-(1-失效率_{1路})\\ &=失效率_{1路}-失效率_{2路} \end{aligned} 平均访存时间伪相联失效率伪相联失效开销伪相联伪命中率=命中时间伪相联+失效率伪相联×失效开销伪相联=失效率2路=失效开销1路=命中率2路−命中率1路=(1−失效率2路)−(1−失效率1路)=失效率1路−失效率2路
因此, 平均访存时 间 伪相联 = 命中时 间 1 路 + ( 失效 率 1 路 − 失效 率 2 路 ) × 2 + 失效 率 2 路 × 失效开 销 1 路 平均访存时间_{伪相联}=命中时间_{1路}+(失效率_{1路}-失效率_{2路})\times 2+失效率_{2路}\times 失效开销_{1路} 平均访存时间伪相联=命中时间1路+(失效率1路−失效率2路)×2+失效率2路×失效开销1路
Cache容量为2KB时,
平均访存时 间 伪相联 = 1 + ( 0.098 − 0.076 ) × 2 + 0.076 × 50 = 4.844 平均访存时间_{伪相联}=1+(0.098-0.076)\times 2+0.076\times 50=4.844 平均访存时间伪相联=1+(0.098−0.076)×2+0.076×50=4.844
Cache容量为128KB时,
平均访存时 间 伪相联 = 1 + ( 0.010 − 0.007 ) × 2 + 0.007 × 50 = 1.356 平均访存时间_{伪相联}=1+(0.010-0.007)\times 2+0.007\times 50=1.356 平均访存时间伪相联=1+(0.010−0.007)×2+0.007×50=1.356
因此,不论Cache容量为2KB还是128KB,三种结构中伪相联速度最快。
(1)
局部不命中 率 L 1 = 40 1000 = 0.04 全局不命中 率 L 1 = 40 1000 = 0.04 局部不命中 率 L 2 = 20 40 = 0.5 全局不命中 率 L 2 = 20 1000 = 0.02 \begin{aligned} 局部不命中率_{L1}=\frac{40}{1000}=0.04\\ 全局不命中率_{L1}=\frac{40}{1000}=0.04\\ 局部不命中率_{L2}=\frac{20}{40}=0.5\\ 全局不命中率_{L2}=\frac{20}{1000}=0.02 \end{aligned} 局部不命中率L1=100040=0.04全局不命中率L1=100040=0.04局部不命中率L2=4020=0.5全局不命中率L2=100020=0.02
(2)
平均访存时间 = 命中时 间 L 1 + 不命中 率 L 1 × ( 命中时 间 L 2 + 不命中 率 L 2 × 不命中开销 ) = 1 + 0.04 × ( 10 + 0.5 × 100 ) = 3.4 \begin{aligned} 平均访存时间&=命中时间_{L1}+不命中率_{L1}\times (命中时间_{L2}+不命中率_{L2}\times 不命中开销)\\ &=1+0.04\times (10+0.5\times 100)=3.4 \end{aligned} 平均访存时间=命中时间L1+不命中率L1×(命中时间L2+不命中率L2×不命中开销)=1+0.04×(10+0.5×100)=3.4
每次访存的平均停顿时间: 3.4 − 1 = 2.4 每次访存的平均停顿时间:3.4-1=2.4 每次访存的平均停顿时间:3.4−1=2.4
每条指令的平均停顿时间 = 每次访存的平均停顿时间 × 每条指令的平均访存次数 = 2.4 × 1.5 = 3.6 \begin{aligned} 每条指令的平均停顿时间 & = 每次访存的平均停顿时间\times 每条指令的平均访存次数\\ &=2.4\times 1.5=3.6 \end{aligned} 每条指令的平均停顿时间=每次访存的平均停顿时间×每条指令的平均访存次数=2.4×1.5=3.6
不命中开 销 直接映像 , L 1 = 10 + 25 % × 50 = 22.5 不命中开 销 两路组相联 , L 1 = 10.1 + 20 % × 50 = 20.1 \begin{aligned} 不命中开销_{直接映像,L1}=10+25\%\times 50=22.5\\ 不命中开销_{两路组相联,L1}=10.1+20\%\times 50=20.1 \end{aligned} 不命中开销直接映像,L1=10+25%×50=22.5不命中开销两路组相联,L1=10.1+20%×50=20.1
对第二级Cache来说,两路组相联不命中开销较小。
for ( i = 0 ; i < 3 ; i = i + 1 )
for ( j = 0 ; j < 100 ; j = j + 1 )
a [ i ][ j ] = b[ j ][ 0 ] * b[ j+1 ][ 0 ];
问:
(1)判断哪些访问可能会导致数据Cache失效;
(2)加入预取指令以减少失效,设预取提前7次循环进行。
(3)计算所执行的预取指令的条数以及通过预取避免的失效次数。
(4)计算节省的时间。忽略指令缓存缺失,并假定数据缓存中没有冲突缺失或容量缺失。假定预取过程可以相互重叠,并能与缓存缺失重叠。因此可以用最高存储器带宽传输。下面是忽略缓存缺失的关键循环次数:原循环每次迭代需要7个时钟周期,第一次预取循环每次迭代需要9个时钟周期,第二次预取循环每次迭代需要8个周期(包含循环外部的开销)。一次缺失需要100个时钟周期。
(1)
- 直接映像Cache的容量大于数组a和b容量之和,因此只会发生强制不命中(失效)。
- a的元素按存储顺序读入,受益于空间局部性,一次读入一个块中含有2个数组元素,因此读入a时,j的偶数值缺失,奇数值命中。所以对a的访问会造成3×(100/2)=150次失效。
- b的元素受益于时间局部性,每次迭代的b[j][0]与上次迭代的b[j+1][0]相同,因此,每当第一次访问b[j][0]时,出现一次缺失,总计101次。
(2)
for ( j = 0; j < 100; j = j+1 ) { prefetch ( b[ j+7 ][ 0 ]); /* 预取7次循环后所需的b ( j , 0 ) */ prefetch ( a[ 0 ][ j+7 ]); /* 预取7次循环后所需的a (0 , j ) */ a [0][ j ] = b[ j ][ 0 ] * b[ j+1 ][ 0 ]; } for ( i = 1; i < 3; i = i+1 ) for ( j = 0; j < 100; j = j+1 ){ prefetch ( a [ i ][ j+7 ]); /* 预取7次循环后所需的a ( i , j ) */ a [ i ][ j ] = b[ j ][ 0 ] * b[ j+1 ][ 0 ]; } /* a失效 7/2 = 4, 两次循环共失效8次 */
(3)
预取a[i][7]至a[i][99],共93×3=279条。
预取b[7][0]至b[100][0],共94条。
将非预取缺失的数目减至:
- 第一次循环中访问元素b[0][0]~b[6][0]的7次缺失。
- 第一,二次循环中访问a[i][0]到a[i][6]的3×([7/2])=12次缺失。(空间局部性)
总共避免了150+101-7-12=232次失效。
(4)
原来的双层嵌套循环执行3x100=300次。由于该循环每次迭代需要7个时钟周期,所以总共为300 × 7= 2100时钟周期再加上缓存缺失。缓存缺失增加 251 x 100=25 100个时钟周期,总共27 200个时钟周期。第一次预取循环迭代 100次,每次迭代为9个时钟周期,所以总共900个时钟周期再加上缓存缺失。现在加上缓存缺失的11x 100-1100个时钟周期,总共2000个时钟周期。第二个循环执行2x100=200次,每次迭代为8个时钟周期,所以需要 1600个时钟周期,再加上缓存缺失8x 100=800个时钟周期。总共2400个时钟周期。由上个例子可以知道,这一代码为了执行这两个循环,在 2000+2400=4400个时钟周期内执行了 400条预取指令。如果我们假定这些预取操作完全与其他执行过程相重叠,那么这一预取代码要快27200/4400=6.2倍。
“仅有的数据访问就是载入和存储,占总指令数的50%。”这句话等价于“每条指令平均访存1.5次”。
所有指令都在缓存中命中时,存储器停顿周期数为0。
C P U 时 间 理想 = I C × C P I × 时钟周期时间 = 1.0 × I C × 时钟周期时间 \begin{aligned} CPU时间_{理想}&=IC\times CPI\times 时钟周期时间\\ &=1.0\times IC\times 时钟周期时间 \end{aligned} CPU时间理想=IC×CPI×时钟周期时间=1.0×IC×时钟周期时间
考虑缺失的情况下:
C P U 时 间 有缺失 = I C × ( C P I e x e c u t i o n + 每条指令的平均访存次数 × 不命中率 × 不命中开销 ) × 时钟周期时间 = I C × ( 1 + 1.5 × 2 % × 25 ) × 时钟周期时间 = 1.75 × I C × 时钟周期时间 \begin{aligned} CPU时间_{有缺失}&=IC\times (CPI_{execution}+每条指令的平均访存次数\times 不命中率\times 不命中开销)\times 时钟周期时间\\ &=IC\times (1+1.5\times 2\%\times 25)\times 时钟周期时间 \\ &=1.75\times IC\times 时钟周期时间 \end{aligned} CPU时间有缺失=IC×(CPIexecution+每条指令的平均访存次数×不命中率×不命中开销)×时钟周期时间=IC×(1+1.5×2%×25)×时钟周期时间=1.75×IC×时钟周期时间
性能是执行时间的倒数:
性 能 理想 性 能 有缺失 = C P U 时 间 有缺失 C P U 时 间 理想 = 1.75 × I C × 时钟周期时间 1.0 × I C × 时钟周期时间 = 1.75 \frac{性能_{理想}}{性能_{有缺失}} =\frac{CPU时间_{有缺失}}{CPU时间_{理想}}=\frac{1.75\times IC\times 时钟周期时间}{1.0\times IC\times 时钟周期时间} =1.75 性能有缺失性能理想=CPU时间理想CPU时间有缺失=1.0×IC×时钟周期时间1.75×IC×时钟周期时间=1.75
因此,当所有指令都在缓存中命中时,计算机可以加快1.75倍。
分离 C a c h e 失效率 = 0.39 % × 75 % + 4.82 % × 25 % = 1.50 % 分离Cache失效率=0.39\%\times 75\%+4.82\%\times 25\%=1.50\% 分离Cache失效率=0.39%×75%+4.82%×25%=1.50%
容量64KB的混合Cache失效率较低,只有1.35%。
平均访存时 间 分离 C a c h e = 1 + 75 % × 0.39 % × 50 + 25 % × 4.82 % × 50 = 1.75 平均访存时 间 混合 C a c h e = 1 + 75 % × 1.35 % × 50 + 25 % × ( 1 + 1.35 % × 50 ) = 1.925 \begin{aligned} 平均访存时间_{分离Cache} & = 1+75\%\times 0.39\%\times 50+25\%\times 4.82\%\times 50 = 1.75\\ 平均访存时间_{混合Cache} & = 1+75\%\times 1.35\%\times 50+25\%\times (1+1.35\%\times 50)=1.925 \end{aligned} 平均访存时间分离Cache平均访存时间混合Cache=1+75%×0.39%×50+25%×4.82%×50=1.75=1+75%×1.35%×50+25%×(1+1.35%×50)=1.925
平均访问时 间 直接映射 = 2 + 1.4 % × 80 = 3.12 n s 平均访问时 间 2 路组相联 = 2.2 + 1.0 % × 80 = 3.0 n s \begin{aligned} 平均访问时间_{直接映射} & = 2+1.4\%\times 80 = 3.12ns\\ 平均访问时间_{2路组相联} & = 2.2+1.0\%\times 80 = 3.0ns \end{aligned} 平均访问时间直接映射平均访问时间2路组相联=2+1.4%×80=3.12ns=2.2+1.0%×80=3.0ns
C P U 时 间 直接映射 = I C × ( 2 + 1.2 × 1.4 % × 80 2 ) × 2 = 5.344 I C C P U 时 间 2 路组相联 = I C × ( 2 + 1.2 × 1.0 % × 80 2.2 ) × 2.2 = 5.36 I C 性 能 直接映射 性 能 2 路组相联 = C P U 时 间 2 路组相联 C P U 时 间 直接映射 = 5.36 I C 5.344 I C = 1.003 \begin{aligned} CPU时间_{直接映射}&=IC\times (2+1.2\times 1.4\%\times \frac{80}{2} )\times 2=5.344IC\\ CPU时间_{2路组相联}&=IC\times (2+1.2\times 1.0\%\times \frac{80}{2.2} )\times 2.2=5.36IC\\ \frac{性能_{直接映射}}{性能_{2路组相联}}&=\frac{CPU时间_{2路组相联}}{CPU时间_{直接映射}}=\frac{5.36IC}{5.344IC}=1.003 \end{aligned} CPU时间直接映射CPU时间2路组相联性能2路组相联性能直接映射=IC×(2+1.2×1.4%×280)×2=5.344IC=IC×(2+1.2×1.0%×2.280)×2.2=5.36IC=CPU时间直接映射CPU时间2路组相联=5.344IC5.36IC=1.003
因此,2路组相联的平均访问时间较短,但是直接映射Cache的CPU性能较高。
(1)
平均访存时 间 伪相联 = 命中时 间 伪相联 + 失效 率 伪相联 × 失效开 销 伪相联 失效 率 伪相联 = 失效 率 2 路 失效开 销 伪相联 = 失效开 销 1 路 伪命中率 = 命中 率 2 路 − 命中 率 1 路 = ( 1 − 失效 率 2 路 ) − ( 1 − 失效 率 1 路 ) = 失效 率 1 路 − 失效 率 2 路 \begin{aligned} 平均访存时间_{伪相联} & = 命中时间_{伪相联}+失效率_{伪相联}\times 失效开销_{伪相联}\\ 失效率_{伪相联}&=失效率_{2路}\\ 失效开销_{伪相联}&=失效开销_{1路}\\ 伪命中率&=命中率_{2路}-命中率_{1路}\\ &=(1-失效率_{2路})-(1-失效率_{1路})\\ &=失效率_{1路}-失效率_{2路} \end{aligned} 平均访存时间伪相联失效率伪相联失效开销伪相联伪命中率=命中时间伪相联+失效率伪相联×失效开销伪相联=失效率2路=失效开销1路=命中率2路−命中率1路=(1−失效率2路)−(1−失效率1路)=失效率1路−失效率2路
因此, 平均访存时 间 伪相联 = 命中时 间 1 路 + ( 失效 率 1 路 − 失效 率 2 路 ) × 1 + 失效 率 2 路 × 失效开 销 1 路 平均访存时间_{伪相联}=命中时间_{1路}+(失效率_{1路}-失效率_{2路})\times 1+失效率_{2路}\times 失效开销_{1路} 平均访存时间伪相联=命中时间1路+(失效率1路−失效率2路)×1+失效率2路×失效开销1路
(2)
平均访存时 间 伪相联, 2 K B = 1 + ( 9.8 % − 7.6 % ) + 7.6 % × 50 = 4.822 平均访存时 间 伪相联, 128 K B = 1 + ( 1.0 % − 0.7 % ) + 0.7 % × 50 = 1.353 \begin{aligned} 平均访存时间_{伪相联,2KB}=1+(9.8\%-7.6\%)+7.6\%\times 50=4.822\\ 平均访存时间_{伪相联,128KB}=1+(1.0\%-0.7\%)+0.7\%\times 50=1.353 \end{aligned} 平均访存时间伪相联,2KB=1+(9.8%−7.6%)+7.6%×50=4.822平均访存时间伪相联,128KB=1+(1.0%−0.7%)+0.7%×50=1.353