当前位置: 首页 > news >正文

计算机系统结构 第二章 :缓存优化

零-1:复习cache的基础知识:

一、映像方式

直接映射

原理:主存被分成若干区,每个区大小与 Cache 相同,区内再分块,主存每个区中块的大小和 Cache 中块大小相等,且每个区包含的块数也与 Cache 中块数相等。主存中任意一个块只能映射到 Cache 中唯一指定的块(相同块号位置) 。主存地址分为区号、块号和块内地址三部分,Cache 地址则分为块号和块内地址。例如,假设主存容量大,有多个区,每个区有若干块,而 Cache 只有一个区,那么主存某区的第 i 块只能对应到 Cache 的第 i 块。

特点:地址变换简单、速度快,可直接由主存地址提取出 Cache 地址。但不灵活,块冲突率较高,当不同区的相同块号的主存块都要调入 Cache 时,会发生冲突,导致 Cache 空间得不到充分利用 。

全相联映射

原理:主存中任何一个块都可以映像装入到 Cache 中的任何一个块的位置上。主存地址和 Cache 地址都分为块号和块内地址两部分。Cache 的块内地址直接取自主存地址的块内地址段,主存块号和 Cache 块号不同,Cache 块号需根据主存块号从块表中查找 。比如主存的任意一块,可放入 Cache 的任意一块位置。

特点:灵活性高,块冲突率低,只有当 Cache 中的块全部装满后才会出现冲突,Cache 利用率高。但地址变换机构复杂,地址变换时需把请求地址同 Cache 中所有地址进行比较来确认,速度慢,成本高 。

组相联映射

原理:是直接映射和全相联映射的折中。主存按 Cache 容量分区,每个区再分为若干组,每组包含若干块,Cache 也进行同样的分组和分块,且主存中一个组内的块数与 Cache 中一个组内的块数相等。组间采用直接映射方式,组内采用全相联映射方式 。即主存某组的块可映射到 Cache 对应组的任意一块中。主存地址包括区号、组号、主存块号和块内地址,Cache 地址包括组号、组内块号和块内地址 。

特点:兼顾了直接映射和全相联映射的优点,地址变换复杂度和块冲突率介于两者之间,是实际中常用的映像方式 。当组的容量为 1 时,就是直接映射;当组的容量等于整个 Cache 的容量时,就是全相联映射 。

二、替换算法

LRU 算法(Least Recently Used)

原理:认为一段时间内最少使用的块,在未来一段时间内也最可能不被使用。当 Cache 已满,需要调入新块时,会淘汰最后一次访问时间最久远的那个块 。例如,记录每个块的最后访问时间,每次有新访问操作时更新对应块的时间,替换时选择时间最久的块淘汰。

特点:命中率相对较高,比较符合程序访问的局部性原理(程序往往会在一段时间内集中访问某些数据),但实现相对复杂,需要额外的硬件或软件机制来记录块的访问时间 。

FIFO 算法(First In First Out )

原理:遵循先入先出原则,当 Cache 被填满,需要替换块时,淘汰最早进入 Cache 的那个块 。就像排队一样,先进入 Cache 的块先被淘汰。

特点:实现简单,只需维护一个队列记录块的进入顺序即可。但缺点是可能会把经常使用但较早进入 Cache 的块淘汰掉,导致命中率不高 。

随机替换算法

原理:当 Cache 需要替换块时,直接随机选择一个块进行淘汰 。

特点:实现非常简单,不需要记录块的访问历史等信息。但命中率较低,因为随机选择不一定能淘汰掉最不常用的块,缺乏对程序访问规律的利用 。

三、写策略

写命中策略

写直达法(全写法,Write Through )

原理:当 CPU 对 Cache 写命中时,必须把数据同时写入 Cache 和主存。一般会使用写缓冲(write buffer)来暂时存储写操作数据,以减少对主存的访问次数 。例如,CPU 要修改 Cache 中某块的数据,在修改 Cache 的同时,将数据也写入主存,若有写缓冲,先写入缓冲,再由缓冲写入主存。

特点:能保证数据的一致性,即 Cache 和主存中的数据时刻保持相同。但每次写操作都要访问主存,会增加访存次数,可能影响系统性能 。

写回法(Write Back )

原理:当 CPU 对 Cache 写命中时,只修改 Cache 的内容,不立即写入主存。只有当此 Cache 块被替换出时才写回主存 。比如 CPU 修改 Cache 中的数据块后,该块继续留在 Cache 中,直到被其他块替换时,才将修改后的数据写回主存。

特点:减少了访存次数,提高了写操作的性能。但存在数据不一致的隐患,在 Cache 块写回主存之前,Cache 和主存中的数据是不一致的 。

写不命中策略

写分配法(Write Allocate )

原理:当 CPU 对 Cache 写不命中时,先从主存中读取相应的块到 Cache 中,然后在这个 Cache 块中进行写入操作 。这是利用了数据的空间局部性原理,即如果 CPU 访问了某个主存块中的数据,那么它很可能在不久的将来再次访问该块或其附近的数据 。

特点:适用于数据存在空间局部性的场景,预先将可能再次被访问的数据块调入 Cache,可以提高后续访问的速度。但会增加 Cache 的写入流量和复杂性 。

写不分配法(Not Write Allocate )

原理:当 CPU 对 Cache 写不命中时,只写入主存,不调入 Cache 。

特点:简化了 Cache 的写操作过程,减少了 Cache 的写入流量。然而,由于写操作没有利用 Cache 的加速效果,可能会导致后续对该数据块的读操作性能下降,因为读操作无法从 Cache 中快速获取数据 。

零-2:复习存储系统的基本知识:


一、存储系统三个相互矛盾的指标

1. 速度

速度指的是存储设备读写数据的快慢程度,通常用访问时间来衡量。访问时间越短,存储设备的速度就越快。例如,寄存器的访问时间通常在几个时钟周期内,能与 CPU 的运算速度相匹配,可快速为 CPU 提供数据;而磁盘的访问时间则达到毫秒级别,速度相对寄存器和内存来说非常慢。

2. 容量

容量是指存储设备能够存储的数据量大小,一般以字节(Byte)为单位,常见的有 KB、MB、GB、TB 等。不同的存储设备容量差异巨大,像寄存器的容量通常只有几十个字节到几百个字节;而磁盘的容量可以达到数 TB。

3. 价格

价格体现了存储设备的成本,通常用每单位容量的价格来衡量。一般来说,速度越快的存储设备,每单位容量的价格越高。例如,高速的 SRAM(静态随机存取存储器)每 GB 的价格远高于低速的磁盘。

矛盾关系

这三个指标相互制约、相互矛盾。要提高存储系统的速度,往往需要采用更先进的技术和材料,这会导致成本增加,价格上升,同时也难以实现大容量。例如,要构建大容量的寄存器存储系统,成本会高得难以承受。若要追求大容量,通常会选择成本较低的存储技术,但其速度会较慢。而若要降低价格,就可能需要牺牲速度和容量。

二、多级存储层次结构

1. 局部性原理

空间局部性:

程序即将用到的信息很可能与现在用到的信息相邻。这是因为程序在执行过程中,通常会顺序访问代码和数据,或者对数组等连续存储的数据结构进行操作。例如,在遍历数组时,会依次访问数组中的相邻元素。

时间局部性:

系统即将要用的信息可能就是现在用的。这是因为程序中存在大量的循环结构和子程序调用,在一段时间内会频繁访问相同的数据和指令。例如,在循环体中,会多次使用循环控制变量和循环体内的数据。

2. 多级存储层次结构的组成及原理

多级存储层次结构一般由寄存器、高速缓存(Cache)、主存(内存)、外存(如磁盘、磁带等)组成。

寄存器:速度最快,但容量最小,价格最贵。它直接集成在 CPU 内部,用于暂时存储 CPU 正在处理的数据和指令,与 CPU 的运算速度相匹配,可快速为 CPU 提供数据。

高速缓存(Cache):速度次之,容量也较小,价格相对较高。它位于 CPU 和主存之间,利用局部性原理,将主存中近期可能会被 CPU 访问的数据和指令复制到 Cache 中。当 CPU 需要访问数据时,先在 Cache 中查找,如果命中,则可以快速获取数据;如果不命中,则从主存中读取数据,并将该数据所在的块调入 Cache 中。这样,通过 Cache 可以减少 CPU 对主存的访问次数,提高存储系统的访问速度。

主存(内存):速度适中,容量较大,价格相对较低。它是计算机运行时程序和数据的主要存储区域,CPU 可以直接访问主存中的数据和指令。

外存(如磁盘、磁带等):速度最慢,但容量最大,价格最便宜。它用于长期存储大量的数据和程序,当需要使用这些数据和程序时,会先将其从外存调入主存中。

3. 多级存储层次结构的优势

通过多级存储层次结构,利用局部性原理,用较少的容量(如 Cache 的容量相对主存和外存来说较小),实现了较快的访问速度和较为实惠的价格。Cache 利用局部性原理,将近期可能访问的数据和指令缓存起来,提高了 CPU 的访问速度;而大容量的外存则以较低的成本提供了长期的数据存储能力。这样,在整体上,存储系统既满足了 CPU 对速度的要求,又满足了用户对大容量存储的需求,同时控制了成本。

越靠近CPU,越贵,越快,越小

一:cache性能分析和改进

时间:

平均访存时间=命中时间+不命中率*不命中开销

CPU时间=(CPU执行周期+存储器停顿周期数)*时钟周期时间

问题:对于一个CPI较小,时钟频率较高的CPU而言,cache不命中影响有双重:

--执行的CPI越小,说明固定周期数的Cache不命中开销就大,影响的指令数量就多

--计算CPI时,不命中的开销是周期数,所以哪怕访存时间一样,频率越高,影响的周期数就越多

所以对于一个低CPI高频率的CPU,Cache不命中代价很大

下面我们根据公式:

从不命中率.不命中开销和命中时间3个方面来考虑优化

二:降低cache的不命中率

(8个方法)

复习3C:

强制不命中(冷启动不命中)

定义:当程序首次访问某个数据块时,由于该数据块还未被调入 Cache,就会发生强制不命中。这种不命中通常发生在程序开始执行、Cache 为空的 “冷启动” 阶段,所以也被称为冷启动不命中。

优化策略:增加块大小。当块大小增加时,每次从主存调入 Cache 的数据量增多,后续访问同一数据块内其他数据时命中的可能性增大。例如,程序顺序访问数组元素,较大的块能一次性将更多相邻元素调入 Cache,减少后续元素访问时的强制不命中。

容量不命中

定义:当程序访问的数据量超过了 Cache 的容量,导致 Cache 无法容纳所有需要的数据,部分数据块会被替换出去,当再次访问这些被替换的数据块时就会发生容量不命中。

优化策略:增加 Cache 容量。更大的 Cache 可以容纳更多的数据块,减少因数据量超过 Cache 容量而导致的替换操作,从而降低容量不命中的概率。比如一个大型矩阵运算程序,若 Cache 容量过小,矩阵元素会频繁被替换出 Cache,增加 Cache 容量就能让更多矩阵元素留在 Cache 中。

冲突不命中

定义:在采用直接映射或组相联映射的 Cache 中,不同的主存块可能会映射到 Cache 的同一位置。当这些主存块需要同时驻留在 Cache 中时,就会发生冲突,导致其中一些块被替换出去,再次访问时就产生冲突不命中。

优化策略:提高相联度。相联度表示一个主存块可以映射到 Cache 中的多少个位置。提高相联度后,主存块的映射位置更加灵活,减少了不同主存块竞争同一 Cache 位置的情况,从而降低冲突不命中的概率。例如,从直接映射(相联度为 1)改为 2 - 路组相联映射,每个主存块有两个 Cache 位置可供选择,冲突的可能性降低。

优化策略带来的负面影响

块大小过大

块数量过少:Cache 的总容量是固定的,块大小增大,块的数量就会减少。这可能导致 Cache 的空间利用率降低,因为一个大的块中可能只有部分数据是程序近期需要访问的,而其他数据是无用的。而且,当发生不命中时,需要从主存调入一个大的块,会增加主存的访问时间和数据传输量。

Cache 命中率下降:如果块大小过大,可能会破坏程序的局部性原理。例如,程序可能只访问一个数据块中的开头部分,但由于块太大,将大量不相关的数据也调入了 Cache,占用了 Cache 空间,使得其他更需要的块无法进入 Cache,反而导致命中率下降。

Cache 容量变大

Cache 访问变慢:随着 Cache 容量的增加,Cache 的物理尺寸增大,信号传输延迟增加。同时,Cache 的地址译码和数据查找电路也会变得更复杂,这都会导致 Cache 的访问时间变长。例如,在大容量 Cache 中查找一个数据块可能需要更多的时钟周期。

相联度提高

硬件复杂度增加:提高相联度意味着在 Cache 查找时需要同时比较更多的 Cache 行。例如,在全相联映射(最高相联度)中,需要将主存地址与 Cache 中所有的块进行比较,这需要更多的比较器和控制电路,增加了硬件设计的复杂度和成本。

访问时间增加:由于需要进行更多的比较操作,相联度提高会使 Cache 的访问时间变长。例如,在一个 4 - 路组相联 Cache 中查找数据的时间通常会比直接映射 Cache 长,因为需要同时比较 4 个 Cache 行。

其他的5个方法:

-1-:伪相联Cache


结合了直接映射和组相联的优点

在逻辑上把直接映像cache空间平分为上下两个区

对于任意一次的访问,按照直接映像cache的方式去处理

如果正常命中,直接返回;否则去找找另一个区域的对应位置,如果找到了,那就是伪命中,否则去下一级存储器

优点是:命中时间短(直接映像),不命中率低(组相联)

缺点是:多种命中时间,我们需要保证大多数的命中都是快速的命中+

平均访存时间=命中时间+不命中率*不命中开销

            =直接命中时间+伪命中率*伪命中开销+不命中率*不命中开销

-2-:硬件预取:

将指令和数据预取放入cache or 外部缓冲器中(通常由cache之外的硬件完成)

两个研究成果:

指令预取

数据预取

如果采用流缓冲器(即可取出数据也可取出指令)

-3-:编译器控制的预取:

在编译期间加入预取指令,在数据被用到之前发出预取请求

做题时要观察规律,并且做好计算

预取的分类

放的位置分:

寄存器预取:把数据放在寄存器

cache预取:把数据只放在cache

预取的处理方式分:

故障性预取:出现虚地址故障or权限问题

非故障性预取:遇到这个预取时,不会发生异常,自动转化为空操作

编译器控制预取的特点:

--1:预取数据的同时要求处理器继续执行(非阻塞cache),要求执行和读取数据重叠执行

--2:循环是预取优化的主要对象

--3:每次预取花费一个指令的开销,我们要保证开销小于收益

-4-:编译器优化:

软件的方式来优化

要求:重新组织程序但是不影响程序的正确性

     如果一个转移指令很可能转移成功,会:把转移目标处的基本块和紧跟着分支指令后的基本块对调,把分支语句的语义取反

     数据对存储位置的限制较少,方便调整顺序

4个技术:

数组合并:

c[i]=x[i]+y[i]

提高空间上的局部性,让x[i]和y[i]同时取出来

内外循环交换

交换后有更好的局部性

循环融合:

重复访问相同的数组合

分块

把数组的按照行or列访问,改为按照块访问,这样访问更集中

同时利用了时间和空间的局部性

-5-:牺牲cache:

在cache和他的下一级存储器的通路之间设置一个全相联的小cache(牺牲cache)用来存放被替换出去的块

三:减少cache的不命中开销

(5个方法)

方法一:多级cache*(2级为例)

一级cache小而快,二级cache大,可以采用较高相联度or伪相联

区分局部不命中率和全局不命中率

这里主要考察做题,计算

方法二:让读不命中优先于写

由于写缓存器的存在,会导致我们读一些正在写回的数据要处理的问题过于复杂

解决办法:

--1:推迟读不命中的处理,知道写缓冲器清空

--2:检查写缓冲器的内容,没有相同的且存储器可用,就继续处理读不命中

方法三:写缓冲合并

处理思路:

如果写缓冲器为空,把数据和地址写入写缓冲器,这个时候cpu方面的写操作完成了

如果写缓冲器不为空,就看看有没有地址匹配的项,并且地址匹配的项的对应位置为空,那么就把这两次操作合并为一次写

如果写缓冲器满且没有可以合并的项,则等

方法四:请求字处理技术

请求字:从下一级存储器调入cache,只有一个字是立即需要的,那么这个字就是请求字

我们希望那请求字给CPU后:CPU今早重启动,请求字没有到达时,CPU等待,一旦请求字到达,就立即给CPU,让它尽早重启的,继续执行

调用块时,让存储器优先提供CPU需要的请求字,一旦到达就送给CPU,让其继续执行.同时从存储器调入该块其他需要的部分

(如果cache较小,下一个指令正好访问cache块的另外一部分,这个技术作用不大)

方法五:非阻塞Cache

采用记分牌orTomasulo算法,允许指令乱序执行,CPU无需再cache不命中时去等待

允许多次不命中,性能更高

并不重叠是不命中次数越多越好,研究表明一次不命中下的命中就可以获得所有的收益

四:减少命中时间

(4个方法)

方法一:使用小容量,结构简单的Cache

因为:性能高维护简便,成本较低。

方法二:虚拟Cache

物理cache需要地址转换和访问cache串行执行,比较缓慢

虚拟cache直接使用虚拟地址访问cache,省去了转换的过程,并且地址转换和访问cache并行,更快更合理

缺点:

需要避免不同进程的相同虚拟地址但是对应不同物理地址的问题(可以引入PID来改善)
也需要注意同义or别名的问题(同一个数据有两个不同虚拟地址下的副本):可以使用禁止or允许(并做好相关的配置)

解决方法(更进一步)

虚拟索引+物理标识:

方法三:cache访问的流水化:

Cache 访问流水化是把对第一级 Cache 的访问过程按照流水线的方式进行组织。流水线技术将一个复杂的操作分解成多个相对简单的子操作,每个子操作在不同的功能段上执行,且这些功能段可以并行工作。在 Cache 访问中,将整个访问过程(如地址译码、数据读取等 )划分为多个步骤,每个步骤在一个时钟周期内完成,这样在每个时钟周期都可以启动一个新的 Cache 访问操作,虽然单个 Cache 访问需要多个时钟周期才能完成,但整体上提高了 Cache 的吞吐率,使系统在单位时间内能够处理更多的 Cache 访问请求 。

方法四:踪迹cache

在追求每个时钟周期流出超过 4 条指令以开发指令级并行性时,难点在于要找到足够多彼此不相关的指令。踪迹 Cache 是应对上述挑战的一种解决方法。它存放 CPU 所执行的动态指令序列,这些序列包含了通过分支预测展开的指令 。分支预测是预测条件分支指令的执行方向,若预测为某个分支,就预先取出该分支上的指令放入踪迹 Cache 。不过分支预测不一定正确,所以在取到指令时需要确认预测是否准确。

优点:能够提高指令 Cache 的空间利用率。因为它根据程序实际执行路径存放指令,避免了普通 Cache 中可能存在的一些无用指令存储(如很少执行的分支上的指令 ),使得 Cache 空间更多用于存放真正会执行的指令。

缺点:

地址映像机制复杂。由于踪迹 Cache 存放的是动态指令序列,其地址映射不能像普通 Cache 那样简单基于静态程序地址,需要考虑分支预测、指令执行顺序等多种因素,增加了硬件实现的复杂度。

相同的指令序列有可能被当作条件分支的不同选择而重复存放。比如一个条件分支有两个可能路径,若分支预测不准确,可能会在踪迹 Cache 中多次存放相同指令序列,造成空间浪费。

相关文章:

  • Java中深拷贝与浅拷贝的深入探讨
  • C++抽象基类三重防线:纯虚函数与保护构造的深度实践
  • springAop代理责任链模式源码解析
  • 《解锁GCC版本升级:开启编程新世界大门》
  • Python蓝桥杯真题代码
  • 工作记录 2015-06-01
  • 数据库介绍以及windows下mysql安装
  • vector和string的迭代器
  • BG开发者日志505:项目总体情况
  • PowerPC架构详解:定义、应用及特点
  • 软件管理(安装方式)
  • MCP 探索:MCP 集成的相关网站 Smithery、PulseMCP 等
  • MySQL安装完全指南:从零开始到配置优化(附避坑指南)
  • 【Python生成器与迭代器】核心原理与实战应用
  • 【Python实战】飞机大战
  • 吾爱出品 [Windows] EndNote 21.5.18513 汉化补丁
  • 【进阶】C# 委托(Delegate)知识点总结归纳
  • Rotary Positional Embedding
  • QT6 源(72):阅读与注释单选框这个类型的按钮 QRadioButton,及各种属性验证,
  • 存在重复元素II(简单)
  • 电商平台集体出手,多措并举助力外贸企业拓内销
  • 中国海警局回应日本民用飞机侵闯我钓鱼岛领空:依法警告驱离
  • 看着不爽就滚蛋!郑州大学第一附属医院一科室公众号被曝运营人员辱骂他人
  • 中央气象台:未来三天北方地区有大风沙尘,江南等地有强降水
  • 海港负国安主场两连败,五强争冠卫冕冠军开始掉队
  • 全球最大汽车板供应商宝钢股份:汽车工业加速转型中材料商如何共舞?