提交 e38db311 编写于 作者: W wizardforcel

ch7.

上级 0d1b60e9
# 第七章 缓存
> 作者:[Allen B. Downey](http://greenteapress.com/wp/)
> 原文:[Chapter 7 Caching](http://greenteapress.com/thinkos/html/thinkos008.html)
> 译者:[飞龙](https://github.com/)
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
## 7.1 程序如何运行
为了理解缓存,你需要理解计算机如何运行程序。你应该学习计算机体系结构来深入理解这个话题。这一章中我的目标是给出一个程序执行的简单模型。
当程序启动时,代码(或者程序文本)通常位于硬盘上。操作系统创建新的进程来运行程序,之后“加载器”将代码从储存器复制到主存中,并且通过调用`main`来启动程序。
在程序运行之中,它的大部分数据都储存在主存中,但是一些数据在寄存器中,它们是CPU上的小型储存单元。这些寄存器包括:
+ 程序计数器(PC),它含有程序下一条指令(在内存中)的地址。
+ 指令寄存器(IR),它含有当前执行的指令的机器码。
+ 栈指针(SP),它含有当前函数栈帧的指针,其中包含函数参数和局部变量。
+ 程序当前使用的存放数据的通用寄存器。
+ 状态寄存器,或者位集训器,含有当前计算的信息。例如,位寄存器通常含有移位来存储上个操作是否是零的结果。
在程序运行之中,CPU执行下列步骤,叫做“指令周期”:
+ 取指(Fetch):从内存中抓取下一条指令,储存在指令寄存器中。
+ 译码(Decode):CPU的一部分叫做“控制单元”,将指令译码,并向CPU的其它部分发送信号。
+ 执行(Execute):收到来自控制单元的信号后会执行合适的计算。
大多数计算机能够执行几百条不同的指令,叫做“指令集”。但是大多数指令可归为几个普遍的分类:
+ 加载:将内存中的值送到寄存器。
+ 算术/逻辑:从寄存器加载操作数,执行算术运算,并将结果储存到寄存器。
+ 储存:将寄存器中的值送到内存。
+ 跳转/分支:修改程序计数器,使控制流跳到程序的另一个位置。分支通常是有条件的,也就是说它会检查位寄存器中的旗标,只在设置时跳转。
一些指令集,包括普遍的x86,提供加载和算术运算的混合指令。
在每个指令周期中,指令从程序文本处读取。另外,普通程序中几乎一半的指令都用于储存或读取数据。计算机体系结构的一个基础问题,“内存瓶颈”就在这里。
在当前的台式机上,CPU通常为2GHz,也就是说每0.5ns就会初始化一条新的语句。但是它用于从内存中传送数据的时间约为10ns。如果CPU需要等10ns来抓取下一条指令,再等10ns来加载数据,它可能需要40个时钟周期来完成一条指令。
## 7.2 缓存性能
这一问题的解决方案,或者至少是一部分的解决方案,就是缓存。“缓存”是CPU上小型、快速的储存空间。在当前的计算机上,储存通常为1~2MiB,访问速度为1~2ns。
当CPU从内存中读取数据时,它将一份副本存到缓存中。如果再次读取相同的数据,CPU就直接读取缓存,不用再等待内存了。
当最后缓存满了的时候,为了能让新的数据进来,我们需要将一些数据扔掉。所以如果CPU加载数据之后,过了一段时间再来读取,数据就可能不在缓存中了。
许多程序的性能受限于缓存的效率。如果CPU所需的数据通常在缓存中,程序可以以CPU的全速来运行。如果CPU时常需要不在缓存中的数据,程序就会受限于内存的速度。
缓存的“命中率”`h`,是内存访问时,在缓存中找到数据的比例。“缺失率”`m`,是内存访问时需要访问内存的比例。如果`Th`是处理缓存命中的时间,`Tm`是缓存未命中的时间,每次内存访问的平均时间是:
```
h * Th + m * Tm
```
同样,我们可以定义“缺失惩罚”,它是处理缓存未命中所需的额外时间,`Tp = Tm - Th`,那么平均访问时间就是:
```
Th + m * Tp
```
当缺失率很低时平均访问时间趋近于`Th`,也就是说,程序可以表现为内存具有缓存的速度那样。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册