目录

计算机组成学习笔记(六)

摘要
计算机组成学习笔记(六)。

7 存储层次结构

7.1 存储层次结构概况

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.1%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E6%A6%82%E5%86%B5/%E5%AD%98%E5%82%A8%E5%99%A8.png
  1. 哪些部件和存储功能有关呢?
  2. 存储器,外部记录介质也是带有存储功能的。
  3. 另外还有一个也带有存储功能那就是运算器。因为CPU当中的通用寄存器是包含在运算器当中的,通用寄存器中也是用来存放临时的数据,也可以看作是一种存储器。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.1%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E6%A6%82%E5%86%B5/%E5%AD%98%E5%82%A8%E5%99%A8%E7%9A%84%E7%89%B9%E6%80%A7.png
存储器的特性
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.1%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E6%A6%82%E5%86%B5/CPU%E4%B8%8E%E5%AD%98%E5%82%A8%E5%99%A8%E7%89%B9%E6%80%A7%E5%AF%B9%E6%AF%941.png
CPU与存储器特性对比1
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.1%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E6%A6%82%E5%86%B5/%E5%AD%98%E5%82%A8%E5%99%A8%E6%80%A7%E8%83%BD%E5%BD%B1%E5%93%8D.png
存储器性能影响
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.1%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E6%A6%82%E5%86%B5/CPU%E4%B8%8E%E5%AD%98%E5%82%A8%E5%99%A8%E7%89%B9%E6%80%A7%E5%AF%B9%E6%AF%942.png
CPU与存储器特性对比2
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.1%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E6%A6%82%E5%86%B5/Cache%E8%AE%BE%E8%AE%A1.png
Cache设计
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.1%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E6%A6%82%E5%86%B5/%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84.png
存储层次结构

7.2 DRAM和SRAM

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.2DRAM%E5%92%8CSRAM/DRAM%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84.png
  1. 这就是我们平常在计算机当中使用的内存条,规范的说法我们应该叫它内存模组。而一个内存模组实际上就是由一块小电路板再加上若干的DRAM芯片构成。
  2. DRAM芯片内部核心的结构就是这个存储阵列,由若干行和若干列构成。如果从外部给入行地址,在给入列地址,那么就可以由此选中一个存储单元,而在一个存储单元当中往往包含着若干个比特, 常见的有四比特或者八比特。每一个比特都采用了这样一个电路的结构, 这个结构我们称为DRAM的一个基本存储单元。
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.2DRAM%E5%92%8CSRAM/DRAM%E5%9F%BA%E6%9C%AC%E5%AD%98%E5%82%A8%E5%8D%95%E5%85%83.png
  1. 这个基本单元就通过这个电容来保存了一个比特的信息。
  2. 那在DRAM芯片当中还有一些地址译码的逻辑,根据外部送入的行地址生成行选择信号, 再根据外部送入的列地址,生成列选择信号,那通过这样的行列选择信号就可以选中对应的存储单元。
  3. 其实也就是将这样的电容联通到外部的输入输出线,那如果是写入就是由外部的数据线驱动对电容进行充电或者放电的操作, 从而完成写入一或者零。那如果是读操作则是由电容对外部的数据信号线进行驱动,从而完成读出零或者一。
  4. 电容是存在漏电效应的,如果经过一段时间电容上的电荷流失过多,我们就会丢失 它所存的信息。因此为了保证DRAM的存储的信息的正确性,我们就需要定期对所有的单元进行刷新。如果这个电容保存的数据信息是一,那么应该补充这个电容的电荷,而如果它原先保存的是数据零,那么也需要通过刷新让它保持无电荷的状态。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.2DRAM%E5%92%8CSRAM/DRAM%E7%9A%84%E7%89%B9%E7%82%B9%E4%B8%8E%E4%B8%BB%E8%A6%81%E7%94%A8%E9%80%94.png
  1. SDRAM,这个S是同步的缩写,那它是在DRAM的基本结构基础上对输入输出接口进行时钟同步,从而提高了读写的效率。
  2. 现在更为常用的内存 则是在SDARAM的基础上又进行了进一步的改进,比如说这些年有DDR1,DDR2,DDR3, 这些不同的内存标准都是在SDRAM的基础上进行的改进和升级。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.2DRAM%E5%92%8CSRAM/SRAM%E5%9F%BA%E6%9C%AC%E5%AD%98%E5%82%A8%E5%8D%95%E5%85%83.png
  1. SRAM的基本结构,这个结构一共有六个晶体管构成。那这六个晶体管合在一起才可以保存一个比特。
  2. 其中BL和BLbar是用于传送读写数据信息的,而wl信号则是用于控制外部是否可以访问这个基本存储单元。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.2DRAM%E5%92%8CSRAM/SRAM%E5%86%99%E5%85%A5%E8%BF%87%E7%A8%8B1.png
  1. 首先这个存储单元中连接的电源和地,分别代表着1和0,只要SRAM处于通电状态,这两个信号的值都会保持稳定。
  2. 如果要写入1,就得将BL的信号的值置为1,而与之相对则需要将BL bar信号置为零,这样就准备好了要写入的数据。
  3. 然后我们需要访问这个SRAM单元,就得把wl信号置为1,一旦wl信号为1,那它所连接的M5和M6这两个晶体管的gate端就都为1。那通过这个图标我们就可以看出M6是一个NMOS晶体管,对于NMOS来说,如果gate端为1,它应该是处于连通状态。因此BL信号上的值就会通过M6传递进来。
  4. 首先它是连到了M2晶体馆的gate端,它是一个PMOS管,PMOS管的gate端如果为1,那就应该处于关闭的状态。那因为M2是关闭的,所以从电源这连过来的1不会被传导出来。
  5. 所以这根信号线上的值到底是什么,我们还得来看下面这个晶体管也就是M1。而M1的gate端和M2的gate端连接的是同一个信号,所以现在也是1,M1是一个NMOS,所以它处于连通状态。所以这根信号上的值就应该是0。
  6. 这根信号线同时连接到了M5的这跟晶体管,而M5的gate端也是由WL信号控制的, 所以M5也是处于联通状态,它会将BL bar信号的0传导进来,那么我们刚才分析了从地传过来的也是0, 这样就没有冲突,这根信号就可以稳定地维持为0。
  7. 现在来看这根信号又控制了哪些晶体管的gate端。首先是M4,M4是一个PMOS管,它的gate端为0的时候,它就处于连通状态,那来自电源的这个1会被传导进来,与此同时M3的gate端也是0,那这根NMOS晶体管就会处于关闭状态。所以这根信号与地是不连通的,那这样从M3和M4的控制来看,这跟信号也应该是1,这和由BL信号传递进来的这个1也是没有冲突的。
  8. 那这样由外部的BL和BL bar输入的信号 就驱动了这个SRAM存储单元的晶体管达到了一个稳定的状态。那因为晶体管的工作速度很快,所以这个SRAM的写入的过程也非常快,那当这几个晶体管状态达到稳定之后我们就可以结束这次写的过程了。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.2DRAM%E5%92%8CSRAM/SRAM%E5%86%99%E5%85%A5%E8%BF%87%E7%A8%8B2.png
  1. 现在我们可以将WL信号由1变为0,从而将M5和M6这两个晶体管关闭,然后就可以撤销BL和BL bar上的信号了。
  2. 在外部的信号撤销之后,那因为M5和M6这两个晶体管都关闭了,所以并不是由外部的BL信驱动M2和M1这两个晶体管的gate端,但是我们注意在刚才写入的过程中,因为M4晶体管处于连通状态,所以电源所传导的这个1会始终驱动这根信号, 保持M2和M1的gate端为1,而只要M2和M1的gate端为1,那么由地传过来的这个0就会保持稳定,而这个0又会反过来驱动M4和M3保持刚才的状态,从而让这个1也保持稳定,那么在这四个晶体管的相互作用下, 那这个SRAM单元就稳定地保持住了刚才写入的这个数据1。
  3. 现在只要WL信号保持为0,这个SRAM 存储单元当中就会始终存储的这个数据1,当然前提是要保持通电的状态。如果断电,电源的这个1消失了,那SRAM当中保存的这个数据也就会丢失。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.2DRAM%E5%92%8CSRAM/SRAM%E8%AF%BB%E5%87%BA%E8%BF%87%E7%A8%8B.png
  1. 还是刚才那个SRAM单元,里面已经保持了一个数据1,那现在外部就来读取这个单元。这还是需要把WL信号置为1, 那这样M5和M6这两个晶体管都会处于连通的状态,而此时外部并不驱动BL和BL bar这两根信号,因此它们将会分别通过M5和M6这两个晶体管被这个SRAM的存储单元所驱动。
  2. BL bar的信号将会被驱动为0,而BL的信号将会被驱动为1。这样外部在接受到这两根信号之后就可以判断出读出的是一个数据1。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.2DRAM%E5%92%8CSRAM/SRAM%E7%9A%84%E5%9F%BA%E6%9C%AC%E7%BB%93%E6%9E%84.png
  1. 如果我们将很多个这样的基本单元组合起来就可以构成一个存储矩阵。
  2. 从外部可以输入若干个地址线,那通过再增加一些地址译码的驱动电路,就可以根据地址信号线的值选中存储矩阵当中指定的若干个SRAM的基本存储单元,由SRAM构成的存储器和DRAM芯片不太一样,它不需要分成行地址和列地址分别选择。
  3. 相对来说SRAM的设计是比较灵活的, 一个地址所对应的存储单元的数量也许是8个比特,10个比特,也可以是32个比特,40个比特,64个比特等等都可以。
  4. 通过地址线选中的一组存储单元之后,我们还要确定到底是需要读还是写,所以外部还需要有读写控制信号,并且有相应的读写控制电路。而且这些读写控制电路还会将外部传来的数据线连接到SRAM的存储单元上,那如果现在是写这样来自我外部数据线的信号去驱动存储单元,如果是读则将存储单元当中的信号驱动外部的数据线, 那这样的电路一般就称为一个SRAM的存储器。
  5. 通常这个SRAM存储器还会有片选信号, 以标明当前是否需要对这个SRAM存储器进行读或者写的操作。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.2DRAM%E5%92%8CSRAM/SRAM%E7%89%B9%E7%82%B9.png
SRAM特点
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.2DRAM%E5%92%8CSRAM/DRAM%E4%B8%8ESRAM%E7%9A%84%E6%AF%94%E8%BE%83.png
DRAM与SRAM的比较

7.3 主存的工作原理

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.3%E4%B8%BB%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/DRAM%E8%8A%AF%E7%89%87%E7%9A%84%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84.png
  1. 在这里,有8个DRAM芯片焊接在这样一块绿色的小电路板上,构成了一个内存模组,这也就是我们通常所说的内存条。
  2. DRAM芯片以一个存储阵列为核心, 这个存储阵列以行列的形式组织,那么行列的交点就是一个存储单元。每个存储单元都有唯一的一组行列地址指定。那么这样一个存储单元一般由若干个比特构成,常见的有4比特或者8比特,而每一个比特都是由这样的一个基本电路构成。
  3. 从外部给入了行地址和列地址之后,这些地址会同时送到每一个DRAM芯片,从而在每个DRAM芯片当中选中对应的一个存储单元。
  4. 对于平板电脑和智能手机,它们本来体积就很小,很难容纳内存插条这样大个的组件,而且它们也不一定需要这么多的DRAM芯片,所以往往是在它们的主板上直接焊接DRAM芯片。
  5. 一般都是由若干个DRAM芯片构成了计算机的主存储器,也被称为内存。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.3%E4%B8%BB%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/SDRAM%E5%86%85%E5%AD%98%E7%9A%84%E8%AE%BF%E9%97%AE%E8%BF%87%E7%A8%8B1.png
  1. 在计算机内部,CPU通过系统总线连接到了内存控制器,而内存控制器再通过系统总线连接到了内存条,内存控制器会把相关的地址线、数据线连接到内存条上的各个DRAM芯片。
  2. 当CPU需要访问存储器时,那首先要申请系统总线,在获得总线控制权后会将地址发到内存控制器中,对于一个32位CPU,那这个地址一般就是32位的。
  3. 在这个时候,地址并不会分成行地址和列地址,而是只有一个地址。然后内存控制器会将这个地址进行分解,形成行地址和列地址等多个部分。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.3%E4%B8%BB%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/SDRAM%E5%86%85%E5%AD%98%E7%9A%84%E8%AE%BF%E9%97%AE%E8%BF%87%E7%A8%8B2.png
  1. 然后内存控制器就会向DRAM芯片发起访存操作。在这一步,可能会包括两个部分,一是称为预充电的这个操作,这里打了中括号,代表了这是一个可能有也可能没有的操作,然后进行行访问,也就是发出行地址。
  2. 通过存储总线发出的行地址会被DRAM芯片当中的 行译码器接收到,就会在存储阵列中选中对应的那一行,然后这一行当中的所有的存储单元的信号都会被经过放大之后放入到一个缓冲区当中,那这个过程就会被称为激活,或者是行访问的过程。
  3. 只有等这个行缓冲区的信号都稳定了,我们才可以进行下一步的操作。因此,我们需要关注的一个时间,就叫做tRCD,这是从行选到列选的延迟。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.3%E4%B8%BB%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/%E8%A1%8C%E9%80%89%E5%88%B0%E5%88%97%E9%80%89%E7%9A%84%E5%BB%B6%E8%BF%9F.png
  1. 假设在这个时钟上升沿,我们发出了行地址,那么我们必须等待tRCD这么长的时间,才可以去进行 下一步的操作,也就是发出列地址。
  2. 这个时间的长短是由这个DRAM芯片本身的特性决定的。一般来说,它的质量越好,这个时间就越短。而对同样一个DRAM芯片,它的工作环境越好,这个时间也会越短。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.3%E4%B8%BB%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/SDRAM%E5%86%85%E5%AD%98%E7%9A%84%E8%AE%BF%E9%97%AE%E8%BF%87%E7%A8%8B3.png
  1. 等tRCD这段时间过去之后,我们就可以发出列地址了。
  2. 这时内存控制器就会把事先准备好的列地址发到DRAM芯片,由列译码器接收。
  3. 那么列译码器收到 列地址之后,就会从缓冲区中选出对应的那一列,如果现在要进行读操作,那被选中的这个存储单元的数,就会送到数据输出接口上去,而从发出列地址,到选出对应的存储单元的数这个过程,就被称为列访问的过程。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.3%E4%B8%BB%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/%E5%88%97%E9%80%89%E5%88%B0%E8%BE%93%E5%87%BA%E6%95%B0%E6%8D%AE%E7%9A%84%E5%BB%B6%E8%BF%9F.png
  1. 列访问也是需要时间的。
  2. 从发出行地址到可以发列地址,中间要等待tRCD这么长的时间,然后从发出列地址到选中的存储单元的数可以输出,也需要等待一段时间,这段时间以CL标记,也就是从列选到数据输出的延迟。
  3. 通常情况下,访问内存都不会只读一个数,而是会连续读出多个数,那么这些数会每一个时钟周期输出一个,依次地送到数据线上。
  4. 正是因为如此,我们要事先把一整行的数, 都读到缓冲区里,因为它可以把每一个存储单元同时都连到了缓冲区中,读出一个存储单元也是读,读出一整行来也是读,所花的时间并没有明显的差别。所以不如一次把一整行都读出来,然后从中选择需要的连续的若干数据送出去,而且这样还有另外一个好处,如果下一次访存还是在这同一行,那就不需要重复发这个行地址了。因为对应的行已经在缓冲区当中,只要直接发列地址就可以。这样就可以大大地缩短访存的时间。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.3%E4%B8%BB%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/SDRAM%E5%86%85%E5%AD%98%E7%9A%84%E8%AE%BF%E9%97%AE%E8%BF%87%E7%A8%8B4.png
  1. 当DRAM芯片送出数据之后,内存控制器就会采样对应的数据,然后将采样到的数据再送回到CPU当中去,那过一段时间CPU又会发出访存的地址,那如果这次要访问的数据 和刚才要访问的数恰好在同一行,那就不需要再重新发行地址,只需要直接发列地址, 从缓冲区中选出对应的单元就可以了。
  2. 如果下一次访问所要的数据不是这一行, 那么就需要把激活的这一行关闭,这个过程我们称为预充电。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.3%E4%B8%BB%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/%E8%AF%BB%E6%93%8D%E4%BD%9C%E6%97%B6%E5%BA%8F%E5%9B%BE.png
  1. 实际上预充电 最早可以在前一次传输,最后一个数据即将送出的时候开始,因为我们不确定下一次传输到底会不会在同一行。所以我们有两种可以选择的策略:
    1. 一种方式是等到新的传输开始,如果发现要访问的数据 不在已经被激活的这一行,那时再进行预充电,这也就是刚才在步骤二中,我们提到的那个可能没有的预充电操作。
    2. 另一种方式,则是在一次传输结束后就进行预充电。这样在下一次的传输是同一行的概率不高的情况下,反而会获得更好的性能。
  2. 预充电也是需要花一定时间的,这个时间我们记为tRP,从内存控制器发出预充电的命令到DRAM芯片可以接收下一次行地址, 这段时间就被称为tRP。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.3%E4%B8%BB%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/SDRAM%E5%85%B3%E9%94%AE%E6%80%A7%E8%83%BD%E5%8F%82%E6%95%B0.png
  • 所以,虽然一个SDRAM有很多的性能参数,但是其中最关键的有这么几个:
    1. tRCD:也就是从行选到列选的延迟时间。
    2. CL:指明了从列选到数据输出的延迟周期数,注意它跟tRCD不同,tRCD规定的是一个时间,单位是纳秒,而CL规定的是一个时钟周期数的延迟。
    3. tRP:也就是行预充电的延迟时间。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.3%E4%B8%BB%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/SDRAM%E8%AE%BF%E9%97%AE%E8%BF%87%E7%A8%8B.png
  1. 当内存控制器发出行地址之后,需要等待tRCD时间,才能发出列地址。
  2. 然后等待CL个时钟周期,DRAM芯片才会送出第一个数据,这时内存控制器才可以采样。
  3. 随后每个周期DRAM芯片都会送出一个数据, 而每个数据的宽度,则取决于内存条上有几个DRAM芯片。
  4. 在最后一个数据送出之前,内存控制器可以发出预充电的命令。
  5. 再经过tRP的时间,就可以发出下一个访存操作的行地址了。

7.4 主存技术的发展

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/SDRAM%E5%85%B8%E5%9E%8B%E8%A7%84%E6%A0%BC.png
SDRAM典型规格
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/SDRAM%E5%86%85%E5%AD%98%E5%B8%A6%E5%AE%BD.png
SDRAM内存带宽
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/%E5%A4%84%E7%90%86%E5%99%A8%E4%B8%8E%E5%AD%98%E5%82%A8%E5%99%A8%E7%9A%84%E6%80%A7%E8%83%BD1.png
处理器与存储器的性能1
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/%E5%BD%B1%E5%93%8DSDRAM%E6%80%A7%E8%83%BD%E7%9A%84%E5%85%B3%E9%94%AE%E5%9B%A0%E7%B4%A0.png
  1. SDRAM的基本单元是一个电容和一个晶体管,对于SDRAM的读写 对于SDRAM的读写最终体现在了对电容的充放电,而要减少这个电容的充放电时间,那是非常困难的。
  2. 因此,在SDR SDRAM的时代,当时钟频率提升到了133MHz之后就很难进一步提升了。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/DDR%E4%B8%8ESDRAM%E7%9A%84%E5%AF%B9%E6%AF%94.png
  1. 对于一个位宽8 比特的SDR SDRAM芯片,每次访问其内部的存储阵列都会取出8比特的数据。
  2. 而同样位宽的DDR SDRAM芯片,每次访问存储阵列则会取出两倍的数据量。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/DDR%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5.png
  1. 双倍数据率:DDR(Double Data Rate)。
  2. 所谓的双倍数据率也就是DDR,相对应的是SDR,SDR是指毎个时钟的上升沿传输数据,接收端也用时钟的上升沿采样数据,而DDR则是在时钟的上升沿和下降沿都传输数据,这样就可以在同样的时钟频率下传输双倍的数据。
  3. DDR指的是这种传输方式, 运用在SDRAM内存上,就有了DDR SDRAM, 但它不仅仅用在内存上,还用在其他很多领域。所以DDR只是一种传输数据的方式,不能将它等价于我们现在用的内存。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/DDR%20SDRAM%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86.png
  1. PC100规格的SDR SDRAM。它的存储阵列的核心时钟频率是100MHz, 对外接口的时钟频率也是100MHz。以读操作为例,每个时钟周期从存储阵列中 读一个数据并送到芯片的接口信号上,从外部看来每个时钟上升沿传输一个数据, 单根数据线上的数据传输率为100MBPS。
  2. 第一代DDR标准及DDR-200, 它的存储阵列的核心时钟频率也是100MHz,但是每个时钟周期读出两个数据,接口的时钟频率 还是100MHz,但因为在两个时钟沿都传输数据, 所以仍然在一个时钟周期内把这两个数据都传输出去了。从外部看来每个时钟上升沿和下降沿各传输一个数据,单根数据线上的数据传输率为200MBPS,这也就是DDR-200的名称由来。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/SDRAM%E7%B3%BB%E5%88%97%E7%9A%84%E9%A2%91%E7%8E%87%E5%92%8C%E5%B8%A6%E5%AE%BD1.png
SDRAM系列的频率和带宽1
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/SDR%E4%B8%8EDDR%E7%9A%84%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%941.png
  1. 以对PC133的读操作为例,大概需要15个纳秒进行行选,再用15个纳秒进行列选,然后才能得到第一个数据。而与这个数据相邻的后续数据则可以在每个时钟周期送出一个,而从发出地址到传输完这四个数据总共需要60ns。
  2. 再来看DDR-200, 因为要从存储阵列中取出双倍的数据,所以对存储阵列的访问变慢了,大概需要20ns的行选,再加20ns的列选,这样才能得到第一个数据。不过在与这个数据相邻的后续数据则可以在每个时钟周期送出两个,因此,送出这四个数据,大约需要20ns。DDR-200宣称的1.6GB/s的传输带宽就是根据最后这一段20ns送出了四个数据计算出来的。
  3. 评价内存的性能要看两个指标。这个指标称为访存带宽,是内存厂商大力宣传的。另一个重要的指标就是读出第一个数据的时间称为访存延迟,因为CPU真正需要的很可能只是第一个数据,所以访存延迟非常关键。这里DDR-200读出第一个数据用了40ns,比PC133还慢了10ns,即使是看读出这四个数据的总时间, 它也用了总共60ns,和PC133是一样的。因此DDR-200的性能表现是要比PC133差的。
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/SDR%E4%B8%8EDDR%E7%9A%84%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%942.png /images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/SDRAM%E7%B3%BB%E5%88%97%E7%9A%84%E6%97%B6%E5%BA%8F%E5%8F%82%E6%95%B0.png

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/DDR2%20SDRAM%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86.png
  • 既然提升核心频率有困难,那就再增加预先取出数据的数量,之前是从存储阵列中取出双倍的数据, 那现在就取出四陪的数据。例如DDR-400,它的核心频率只有100MHz, 和DDR-200一样,但因为预取了四个数据,从外界看来数据传输率达到了400MBPS。当然这时候单靠双倍数据率的传输也不够了。所以它的接口时钟频率也提升了一倍,变成了200MHz。这样上下始终沿都传输数据,正好在核心时钟的一个周期内把四个数据都送出去了。
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/SDRAM%E7%B3%BB%E5%88%97%E7%9A%84%E9%A2%91%E7%8E%87%E5%92%8C%E5%B8%A6%E5%AE%BD2.png

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/SDRAM%E7%B3%BB%E5%88%97%E7%9A%84%E9%A2%91%E7%8E%87%E5%92%8C%E5%B8%A6%E5%AE%BD3.png

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/SDRAM%E6%97%B6%E5%BA%8F%E5%8F%82%E6%95%B0tRCD.png /images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/SDRAM%E6%97%B6%E5%BA%8F%E5%8F%82%E6%95%B0CL.png
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.4%E4%B8%BB%E5%AD%98%E6%8A%80%E6%9C%AF%E7%9A%84%E5%8F%91%E5%B1%95/%E5%A4%84%E7%90%86%E5%99%A8%E4%B8%8E%E5%AD%98%E5%82%A8%E5%99%A8%E7%9A%84%E6%80%A7%E8%83%BD2.png
处理器与存储器的性能2

7.5 高速缓存的工作原理

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84.png
存储层次结构
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/%E7%A8%8B%E5%BA%8F%E7%9A%84%E5%B1%80%E9%83%A8%E6%80%A7%E5%8E%9F%E7%90%86.png
  • 程序的局部性原理:
    1. 时间局部性:变量是保存在内存中的,那它所在的这个存储单元,就会不断的被访问。
    2. 空间局部性:正在被访问的 存储器单元附近的那些数据,也很快会被访问到。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/Cache%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86.png
  1. Cache就是利用了程序的局部性原理,而设计出来的:
    1. 空间局部性的利用:
      1. 当CPU要访问主存时,试讲时把地址发给了Cache,最开始,Cache里面是没有数据的,所以Cache会把地址再发给主存,然后从主存中- 取得对应的数据,但Cache并不会只取回CPU当前需要的那个数据,而是会把与这个数据,位置相邻的主存单元里的数据,一并取回来, 这些数据就称为一个数据块。
      2. Cache会从主存里,取回这么一个数据块,存放在自己内部,然后再选出CPU需要- 的那个数据送出去,那过一会儿,CPU很可能就会需要刚才那个数据附近的其它数据, 那这时候,这些数据已经再Cache当中了,就可以很快的返回,从而提升了访存的性能。
    2. 时间局部性的利用:
      • 这个数据块暂时会保存在Cache当中,那CPU如果要再次用到刚才用过的那个存储单元,那Cache也可以很快的提供这个单元的数据,这就是Cache对程序局部性的利用。
  2. 这些操作都是由硬件完成的,对于软件编程人员来说,他编写的程序代码当中,只是访问了主存当中的某个地址,而并不知道这个地址对应的存储单元,到底是放在主存当中,还是在Cache当中。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/Cache%E7%9A%84%E8%AE%BF%E9%97%AE%E8%BF%87%E7%A8%8B.png
  1. CPU发出度请求,直接发给Cache。
  2. Cache这个硬件的部件,会检查这个数据,是否在Cache当中,如果是,就称为Cache命中,然后从Cache当中取出对应的数据,返回给CPU就可以了。
  3. 如果这个数不在Cache中,我们就称为Cache失效, 那这时候,就要由Cache这个部件,向主存发起读请求,这个过程CPU是不知情的,它仍然是在等待Cache返回数据,那Cache向主存发出读请求之后,就会等待主存将数据返回,这个过程会很长,那么当包含这个数据的一整个数据块返回之后,Cache就会更新自己内部的内容,并将CPU需要的那个数据返回给CPU,这样就完成了一次Cache读的操作。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/Cache%E7%BB%84%E7%BB%87%E7%BB%93%E6%9E%84%E7%A4%BA%E4%BE%8B.png
  1. Cache主要组成部分是一块SRAM,当然还有配套的一些控制逻辑电路。
  2. 它会分为很多行,那么在这个事例的结构当中,一共有16行, 每一行当中有一个比特,是有效位,还有若干个比特是标签,然后其它的位置,都是用来存放从内存取回来的数据块。在这个例子当中,一个数据块是16个字节。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/Cache%E8%AF%BB%E6%93%8D%E4%BD%9C%E8%BF%87%E7%A8%8B%E7%A4%BA%E4%BE%8B.png
Cache读操作过程示例
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/Cache%E8%AF%BB%E6%93%8D%E4%BD%9C%E8%BF%87%E7%A8%8B%E7%A4%BA%E4%BE%8B1.png
  1. 访问2011H这个内存地址,并取出对应的字节,放在AL寄存器当中去,那CPU就会把这个地址发给Cache,那因为现在Cache全是空的,所以显然没有命中,那Cache就会向内存发起一次读操作的请求。
  2. Cache向主存发出的地址,是2010H, 这个地址是16个字节对齐的,而且从它开始的16个字节的这个数据块当中,包含了2011H这个地址单元,当Cache把这个数据块读回来之后,会分配到表项1中,那么在这个表项当中,这个字节就是2010所对应的数据,这个字节就是2011所对应的数据, 所以Cache会将这个字节返回给CPU。
  3. CPU在执行这条指令的时候,Cache收到的地址,实际上是2011H,那因为现在一个数据块当中,包括16个字节,那最后的这个16进制数,正好用来指令这16个字节当中的哪一个字节,是当前所需的,因此,我们取回的这个数据块,要放在哪一个表项当中,就要靠前面的一个地址来决定。那么在现在的Cache设计当中,一般来说,都是用剩下的这些地址当中,最低的那几位,来决定到底把这个数据块放在哪一个Cache行中,那我们现在由16个表项,所以也需要4位的地址来决定,那因此,现在剩下的最低的4位地址,就正好是这个16进制数了。
  4. 这个数是1,所以Cache就决定把这个数据块放在表项1的Cache行里,那现在还剩下8位的地址,我们也必须记录下来,不然以后就搞不清楚,这个Cache行里存放的数据,到底是对应哪一个地址的,所以剩下的地址,不管有多少,都要存放在标签这个域当中。
  5. 在把数据块取回之后,还需要把这个有效位置为1,那这样,我们通过这个表格,就可以明确的知道,当前的这个数据块,是从2010这个地址读出来的,那在这个Cache行中的第一个字节,就是CPU现在做需要的那个字节了。那把这个字节取出来,交给CPU,这条指令对应的读操作就完成了。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/Cache%E8%AF%BB%E6%93%8D%E4%BD%9C%E8%BF%87%E7%A8%8B%E7%A4%BA%E4%BE%8B21.png
  1. Cache收到4011这个地址后,首先应该找到这个地址对应的Cache行在哪里,那它会用这一部分的地址来索引行,所以找到的还是表项1,然后检查有效位,是1,代表这一行当中的数据是有效的。
  2. 这并不能说明它所需要的数据就在这一行里面。接下来还需要比较标签位,那把地址当中的高位40H和这个标签位进行比较,结果发现不相等,那就说明这行当中的数据不是4011对应的那个数据块, 因此Cache还是需要向储存放出访存请求。
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/Cache%E8%AF%BB%E6%93%8D%E4%BD%9C%E8%BF%87%E7%A8%8B%E7%A4%BA%E4%BE%8B22.png
  1. 等会取回的这个数据块,也还是需要放在这个表项1当中,所以会覆盖现在Cache当中的这个数据块,而且等会还会把这个标签位也改成40H。
  2. 现在还是第一个字节,把这个字节B1H返回给CPU,就完成了这条指令的读操作了。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/Cache%E8%AF%BB%E6%93%8D%E4%BD%9C%E8%BF%87%E7%A8%8B%E7%A4%BA%E4%BE%8B30.png
  • 这一次会访问表项3,然后是不命中的,然后读取内存地址3730的数据块,并填到表项3中,然后返回其中第二个字节C2H,这样就完成了第三条指令。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/Cache%E8%AF%BB%E6%93%8D%E4%BD%9C%E8%BF%87%E7%A8%8B%E7%A4%BA%E4%BE%8B40.png
  1. 第四条指令,这条指令的地址是401F,那么Cache会首先找到对应的行,因为这部分地址是1,所以索引到的还是表项1 ,然后查看有效位,确定这一行的数据是有效的。
  2. 下一步是比较标签,那么都是40,所以标签也是匹配的,这样就可以确认这个地址对应的数据就在这个Cache行当中,那我们就称为Cache命中,最后再根据地址的最低几位,找到对应的字节, 那这个BFH就是CPU需要的这个数据了,把这个数据返回之后,这条指令也就完成了。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/Cachet%E7%9A%84%E5%86%99%E7%AD%96%E7%95%A5.png
  1. 当CPU要写一个数据的时候,也会先送到Cache, 命中情况:
    1. 如果Cache命中,我们可以采用一种叫写穿透的策略,把这个数据写入Cache中命中的那一行的同时,也写入主存当中对应的单元, 那这样就保证了Cache中的数据和主存中的数据始终是一致的。但是因为访问主存比较慢,这样的操作效率是比较低的。
    2. 所以我们还可以用另一种策略叫做写返回,那这时只需要把数据写到Cache当中,而不写回主存, 只有当这个数据块被替换的时候,才把数据写回主存。那这样做的性能显然会好一些, 因为有可能会对同一个单元连续进行多次的写,那这样只用将最后一次写的结果在替换时,写回主存就可以了,大大减少了访问主存的次数。但是要完成这样的功能,Cache这个部件就会变得复杂得多。
  2. 当CPU要写一个数据的时候,也会先送到Cache, 失效情况:
    1. “写不分配”:因为Cache失效,所以要写的那个存储单元不在Cache当中,写不分配的策略就是直接将这个数据写到对应的主存单元里。实现起来是很简单的,但是性能并不太好。
    2. “写分配”:那就是先将这个数据所在的数据块读到Cache里以后,再将数据写到Cache里。写分配的策略,虽然第一次写的时候操作复杂一些,还是要将这个块读到Cache里以后再写入,看起来比写不分配要慢一点, 但是根据局部性的原理,现在写过的这个数据块过一会很可能会被使用,所以提前 把它分配到Cache当中后,会让后续的访存性能大大提升。
  3. 在现代的Cache设计当中,写穿透和写不 分配这两种比较简单的策略往往是配套使用的,用于那些对性能要求不高,但是希望设计比较简单的系统。
  4. 而大多数希望性能比较好的Cache,都会采用写返回和写分配这一套组合的策略。

7.6 高速缓存的组织结构

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.6%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E7%BB%84%E7%BB%87%E7%BB%93%E6%9E%84/Cache%E7%9A%84%E8%AE%BF%E9%97%AE%E8%BF%87%E7%A8%8B.png
  1. 从Cache中将命中的数据返回的时间,就称为命中时间, 这也是一个重要的性能参数。
  2. 现在的CPU当中,一级开始的命中时间大约是1到3 个周期,二级开始的命中时间大约是5到20个周期。
  3. 如果数据不在Cache里,我们就称为Cache失效,那对应的就是失效率。失效率和命中率加在一起,肯定是百分之一百。那在失效之后,Cache会向主存发起读请求,那么等待从主存中返回读数据的这个时间,我们就称为失效代价。现在通常需要在100到300个时钟周期之后,才能得到读的数据。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.6%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E7%BB%84%E7%BB%87%E7%BB%93%E6%9E%84/%E5%B9%B3%E5%9D%87%E8%AE%BF%E5%AD%98%E6%97%B6%E9%97%B4.png
  1. 平均访存时间就等于命中时间,加上失效代价乘以失效率。
  2. 想要提高访存的性能,我们就得降低平均访存时间,那要做的就是分别降低这三个参数。
  3. 其中想要降低命中时间,就要尽量将Cache的容量做得小一些, Cache的结构也不要做得太复杂。
  4. 但是小容量的结构简单的Cache,又很容易发生失效,这样就会增加平均访存时间。
  5. 其中如果要减少失效代价,要么是提升主存的性能,要么是在当前的高速缓存和主存之间再增加一级高速缓存。那在新增的那级高速缓存当中,也需要面临这些问题。
  6. 如果有的Cache的命中率是97%,我们假设命中时间是3个周期,失效代价是300个周期,那么平均访存时间就是12个周期。那如果我们有一个方法可以在不影响命中时间和失效代价的情况下,将命中率提高到99%, 那这时候的平均访存时间就降低到了6个周期。所以虽然命中率只提高了2%,看起来并不起眼, 但是访存性能却提高了一倍,这是非常大幅度的提升。所以对于现在的Cache来说,能够提高一点点命中率,都可以带来很好的性能提升。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.6%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E7%BB%84%E7%BB%87%E7%BB%93%E6%9E%84/Cache%E5%A4%B1%E6%95%88%E5%8E%9F%E5%9B%A0.png
  1. 义务失效,那从来没有访问过的数据块,肯定就不在Cache里, 所以第一次访问这个数据块所发生的失效,就称为义务失效,义务失效是很难避免的。
  2. 容量失效,那如果这个程序现在所需的数据块已经超过了这个Cache容量的总和,那不管我们怎么去精巧地设计这个Cache,总会发生失效。当然容量失效可以通过扩大Cache来缓解, 但是增加了Cache容量之后,一方面会增加成本,另一方面可能也会影响到命中时间,所以也需要综合地考虑。
  3. 冲突失效,也就是在Cache并没有满的情况下,因为我们将多个存储器的位置映射到了同一个Cache行,导致位置上的冲突而带来的失效。这个问题就成为Cache的映射策略。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.6%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E7%BB%84%E7%BB%87%E7%BB%93%E6%9E%84/Cache%E7%9A%84%E6%98%A0%E5%B0%84%E7%AD%96%E7%95%A5.png
  1. 这是一块存储器的区域,我们还是按照每16个 字节一个数据块的形式把这块存储器的区域画出来,所以这里标记的是这个数据块的起始地址, 每个地址之间正好相差16。
  2. 如果我们有一个8表项的Cache,那么就采用我们之前介绍过的那种存放方法。地址为0的数据块是要发在表项0当中, 而地址为080的数据块,也得放在表项0当中,同样地址为100的数据块,也要存放在这个表项中。这其实就是把内存分成每8个数据块为一组,任何一组当中的第0个数据块,都会被放在表项0,第一个数据块都会被放在表项1, 那这样的Cache的映射策略就叫做直接映射。它的优点在于硬件结构非常简单, 我们只要根据地址就可以知道对应的数据块应该放在哪个表项。但是它的问题也很明显,如果我们在程序当中正好要交替地不断访问两个数据,CPU不断地在交替访问数据A和数据B,那这段时间的Cache访问每一次都是不命中的,这样的访存性能还不如没有Cache,而这时Cache当中其他的行,都还是空着的,根本没有发挥作用。
  3. 再做一些改进,在不增加 Cache总的容量情况下,我们可以将这8个Cache行分为两组,这就是二路组相联的Cache。那这样刚才那种交替地访问数据A和数据B的情况,就不会有问题了。
  4. 如果CPU在交替地访问这三个数据块当中的数据,那么二路组相联的Cache又会出现连续不命中的情况,所以我们还可以对它进一步切分。这就是一个四路组相联的Cache。
  5. 那我们是不是可以无限制地切分下去呢?这倒是可以的,这样结构的Cache就叫做全相联的Cache。这样的设计灵活性显然是最高的,但是它的控制逻辑也会变得非常的复杂。
  6. 假设CPU发了一个地址,Cache要判断这个地址是否在自己内部,那它就需要把可能包含这个地址的Cache行当中的标签取出来进行比较,对于直接映射的Cache,只需要取一个标签来比较就行, 二路组相联的时候,就需要取两个标签同时进行比较,四路组相联的时候就需要取出四个标签来比较, 而在全相联的情况下,那就需要把所有行当中的标签都取出来比较。这样的比较需要选用大量的硬件电路,既增加了延迟,又增加了功耗,如果划分的路数太多,虽然有可能降低了失效率,但是却增加了命中时间,这样就得不偿失了。而且话又说回来,增加了路数,还不一定能够降低失效率。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.6%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E7%BB%84%E7%BB%87%E7%BB%93%E6%9E%84/%E5%B8%B8%E8%A7%81%E7%9A%84Cache%E6%9B%BF%E6%8D%A2%E7%AE%97%E6%B3%95.png
  1. 在多路组相联的Cache当中,同一个数据块可以放在不同的地方,那如果这些地方都已经被占用了,就需要去选择一行替换出去,这个替换算法设计得好不好,就对性能有很大的影响。如果这个Cache选择替换出去的行,恰恰总是过一会就要使用到的那个数据块,那这样性能的表现就会很差。
  2. 最简单的是随机替换,这个性能显然不会很好。
  3. 轮转替换,也就是按照事先设定好的顺序依次地进行替换,但是性能也一般。
  4. 性能比较好的替换算法,是最近最少使用的替换算法,简称为LRU,那它需要额外的硬件来记录访问的历史信息,在替换的时候,选择距离现在最长时间没有被访问的那个Cache行进行替换,在使用中,这种方法的性能表现比较好,但是其硬件的设计也相当的复杂。
  5. 映射策略和替换算法都需要在性能和实现代价之间进行权衡。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.6%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E7%BB%84%E7%BB%87%E7%BB%93%E6%9E%84/80486%E7%9A%84%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98.png
  1. 在X86系列CPU当中,486是最早在CPU芯片内部集成了Cache的,但它使用的是一个指令和数据共用的Cache。
  2. 这个Cache有一个很明显的缺点,那就是指令和数据的局部性会互相影响,因为指令和数据一般是存放在内存中的不同区域,所以它们各自具有局部性。
  3. 在执行一个要操作大量数据的程序,那这些数据就会很快地占满Cache,把其中的指令都挤出去了,那在这个时候,执行一条指令,取指的阶段很可能是Cache不命中,需要等待访问存储器,那就需要花很长的时间,而在执行阶段,去取操作数时,却往往会命中Cache,那虽然这段时间比较短,但是整个指令执行的时间还是很长。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.6%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E7%BB%84%E7%BB%87%E7%BB%93%E6%9E%84/Pentium%E7%9A%84%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98.png
  • 后来的奔腾,就把指令和数据分成了两个独立的Cache,这样它们各自的局部性就不会相互影响了,现在大多数CPU的一级Cache都会采用这样的形式。

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.6%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E7%9A%84%E7%BB%84%E7%BB%87%E7%BB%93%E6%9E%84/Core%20i7%E7%9A%84%E5%A4%9A%E7%BA%A7%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98.png
  1. 这个是现在比较先进的Core i7,它内部采用了多级Cache的结构。
  2. 一级Cache是指令和数据分离的各32K个Byte,采用了8路组相联的形式,命中时间是4个周期,所以在CPU的流水线当中,访问Cache也需要占多个流水席。
  3. 二级Cache就不再分成指令和数据两个部分了,因为它的容量比较大,指令和数据之间的相互影响就不那么明显。但是二级Cache的命中时间也比较长,需要11个周期,那i7 CPU的流水线总共也就16级左右,肯定是没有办法和二级Cache直接协同工作的。这也是为什么一级Cache不能做得很大的一个重要原因。
  4. 在二级Cache之下,还有一个三级Cache,这是由四个核共享的,总共8兆个字节,三级Cache采用了16路组相联的结构,而且容量也很大,达到了8兆个字节,这又导致它的命中时间很长,需要30到40个周期,但它这样的结构命中率会很高,这样就很少需要去访问主存了。

7.7 存储容量的计算

/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.7%E5%AD%98%E5%82%A8%E5%AE%B9%E9%87%8F%E7%9A%84%E8%AE%A1%E7%AE%97/%E5%A5%87%E6%80%AA%E7%9A%84%E4%BC%98%E7%9B%981.png
奇怪的优盘1
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.7%E5%AD%98%E5%82%A8%E5%AE%B9%E9%87%8F%E7%9A%84%E8%AE%A1%E7%AE%97/%E5%A5%87%E6%80%AA%E7%9A%84%E4%BC%98%E7%9B%982.png
奇怪的优盘2
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.7%E5%AD%98%E5%82%A8%E5%AE%B9%E9%87%8F%E7%9A%84%E8%AE%A1%E7%AE%97/%E5%B8%B8%E7%94%A8%E7%9A%84%E5%8D%95%E4%BD%8D%E5%89%8D%E7%BC%80.png
常用的单位前缀
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.7%E5%AD%98%E5%82%A8%E5%AE%B9%E9%87%8F%E7%9A%84%E8%AE%A1%E7%AE%97/%E5%9B%BD%E9%99%85%E5%8D%95%E4%BD%8D%E5%88%B6.png
国际单位制
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.7%E5%AD%98%E5%82%A8%E5%AE%B9%E9%87%8F%E7%9A%84%E8%AE%A1%E7%AE%97/%E5%9B%BD%E9%99%85%E5%8D%95%E4%BD%8D%E5%88%B61.png
国际单位制1
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.7%E5%AD%98%E5%82%A8%E5%AE%B9%E9%87%8F%E7%9A%84%E8%AE%A1%E7%AE%97/%E5%9B%BD%E9%99%85%E5%8D%95%E4%BD%8D%E5%88%B62.png
国际单位制2
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.7%E5%AD%98%E5%82%A8%E5%AE%B9%E9%87%8F%E7%9A%84%E8%AE%A1%E7%AE%97/%E5%9B%BD%E9%99%85%E5%8D%95%E4%BD%8D%E5%88%B63.png
国际单位制3
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.7%E5%AD%98%E5%82%A8%E5%AE%B9%E9%87%8F%E7%9A%84%E8%AE%A1%E7%AE%97/%E5%9B%BD%E9%99%85%E5%8D%95%E4%BD%8D%E5%88%B64.png
国际单位制4
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.7%E5%AD%98%E5%82%A8%E5%AE%B9%E9%87%8F%E7%9A%84%E8%AE%A1%E7%AE%97/%E8%AE%A1%E7%AE%97%E6%9C%BA%E9%A2%86%E5%9F%9F%E7%9A%84%E4%BD%BF%E7%94%A8%E6%83%85%E6%B3%81.png
计算机领域的使用情況
/images/Computer_Organization/7存储层次结构/7.7存储容量的计算/ANSI/IEEE的定义.png
ANSI/IEEE的定义
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.7%E5%AD%98%E5%82%A8%E5%AE%B9%E9%87%8F%E7%9A%84%E8%AE%A1%E7%AE%97/NIST%E7%9A%84%E5%A3%B0%E6%98%8E.png
NIST的声明
/images/Computer_Organization/7%E5%AD%98%E5%82%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84/7.7%E5%AD%98%E5%82%A8%E5%AE%B9%E9%87%8F%E7%9A%84%E8%AE%A1%E7%AE%97/%E7%9C%9F%E5%AE%9E%E5%9B%BD%E6%A0%87%E5%8D%95%E4%BD%8D.png
真实国标单位