# 缓存性能回顾

由于局部性和较小存储器的速度较高，存储器的层次结构可以大幅度提高性能。评估高速缓存性能的一个方法是扩展第一章中的处理器执行时间方程。我们现在考虑到处理器在等待内存访问时停滞的周期数，我们称之为内存停滞周期（memory stall cycles）。那么，性能就是时钟周期时间与处理器周期和内存停滞周期之和的乘积：

![](https://3404518874-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdXvFTNpUgIWBzBBchio%2Fuploads%2Fntqf0FrHuggrwLld9HhE%2FNeatReader-1656493288625.png?alt=media\&token=a071bdf8-b397-4927-8315-0e9daf43cd99)

这个等式假设CPU的时钟周期包括处理高速缓存命中的时间，并且处理器在高速缓存缺失期间是停滞的。B.2节重新审视了这个简化的假设。

内存停滞周期的数量取决于失误的数量和每次失误的成本，这被称为未命中惩罚（miss penalty）:

![](https://3404518874-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdXvFTNpUgIWBzBBchio%2Fuploads%2FmL2LE6PAo8uAB2rXVZy7%2FNeatReader-1656493705674.png?alt=media\&token=b477f976-8ad0-445a-b8d5-d185bb4cc323)

最后一种形式的优点是，可以很容易地测量各个部分。我们已经知道如何测量指令数（IC）。（ 对于推测性处理器，我们只计算提交的指令）测量每条指令的内存引用数量也可以用同样的方式完成；每条指令都需要一个指令访问，而且很容易决定它是否也需要一个数据访问。

请注意，我们计算未命中惩罚是一个平均值，但在这里我们将把它当作一个常数来使用。由于先前的内存请求或内存刷新，高速缓存后面的内存在未命中的时候可能很忙。在处理器、总线和内存的不同时钟之间的接口处，时钟周期的数量也会有所不同。因此，请记住，用一个单一的数字来表示失误惩罚是一种简化。

在上面的式子中，未命中率部分是导致未命中的高速缓存访问的一部分（即，未命中的访问数除以总访问数）。未命中率可以用高速缓存模拟器来测量，该模拟器获取指令和数据引用的地址轨迹，模拟高速缓存行为以确定哪些引用命中，哪些引用缺失，然后报告命中和未命中总数。今天，许多微处理器提供了硬件来计算失误和内存引用的数量，这是一个更容易和更快的方法来测量未命中率。

前面的公式是一个近似值，因为读和写的未命中率和未命中惩罚往往是不同的。可以用每条指令的内存访问次数、读和写的未命中惩罚（以时钟周期为单位）以及读和写的未命中率来定义内存滞后时钟周期：

![](https://3404518874-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdXvFTNpUgIWBzBBchio%2Fuploads%2FpKSOIeC3Q0V880LSAYPy%2FNeatReader-1656494474009.png?alt=media\&token=a591ff84-c5e0-4dd4-ad1a-9a31518ac49b)

我们通常通过合并读和写，找到读和写的平均未命中率和未命中惩罚来简化完整的公式：

![](https://3404518874-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdXvFTNpUgIWBzBBchio%2Fuploads%2FVIKGp14IVmCXMYMolDes%2FNeatReader-1656494595169.png?alt=media\&token=14caea25-397d-41f5-943a-237bf7de325f)

未命中率是缓存设计中最重要的衡量标准之一，但是，正如我们在后面的章节中所看到的，它并不是唯一的衡量标准。

示例：假设我们有一台计算机，当所有的内存访问都在高速缓存中时，每条指令的周期（CPI）是1.0。唯一的数据访问是加载和存储，而这些访问占指令总数的50%。如果未命中惩罚是50个时钟周期，未命中率是1%，如果所有的指令都在高速缓存中命中，那么计算机的速度会快多少？

答案：首先计算一下全都命中的计算机的性能：

![](https://3404518874-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdXvFTNpUgIWBzBBchio%2Fuploads%2FBhsVlN5kcgKIflLEITK8%2FNeatReader-1656574241439.png?alt=media\&token=8733c341-8416-4f8c-9a52-23c2f16c8bd6)

现在对于有真正缓存的计算机，首先我们计算内存滞留周期：

![](https://3404518874-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdXvFTNpUgIWBzBBchio%2Fuploads%2F6RFukcmraLgaecN39YNJ%2FNeatReader-1656574280092.png?alt=media\&token=83e25170-3364-41ec-bb36-d17586519d19)

其中中间项（1+0.5）代表每条指令的1个指令访问和0.5个数据访问。因此，总的性能是：

![](https://3404518874-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdXvFTNpUgIWBzBBchio%2Fuploads%2FzgHzSSWuOfjDBx8vlqJM%2FNeatReader-1656574355157.png?alt=media\&token=6643e7ef-e775-4c12-a77e-56b86a1fd922)

性能比是执行时间的倒数：

![](https://3404518874-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdXvFTNpUgIWBzBBchio%2Fuploads%2FB7YK9lGLuQWhGuD4cUQb%2FNeatReader-1656574378708.png?alt=media\&token=3a5178a5-710f-47e8-9034-38c1241dc8f4)

也就是说，速度是存在未命中计算机的1.75倍。

一些设计者喜欢用每条指令的未命中率来衡量整体的未命中率，而不是每条内存引用的未命中率。这两者是相关的：

![](https://3404518874-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdXvFTNpUgIWBzBBchio%2Fuploads%2FoNGjCK8epEzCPlwcuBco%2FNeatReader-1656574934509.png?alt=media\&token=be2f8449-3603-4152-ae90-94a03769800b)

当你知道每条指令的平均内存访问次数时，后一个公式很有用，因为它允许你将未命中率转换成每条指令的未命中，反之亦然。例如，我们可以把前面例子中每个内存引用的未命中率转化为每个指令的未命中率：

![](https://3404518874-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdXvFTNpUgIWBzBBchio%2Fuploads%2F5An4DAzWIjoCqk6Ho9Jn%2FNeatReader-1656575156094.png?alt=media\&token=58f21328-61fd-45ab-b50a-d788d47638dd)

顺便说一下，每条指令的未命中率通常被报告为每1000条指令的未命中率，以显示整数而不是分数。因此，前面的答案也可以表示为每1000条指令30次未命中。

使用“每条指令未命中”来表述的优点是它与硬件实现无关。例如，推测性处理器（speculative processors）获取的指令数量是实际提交的指令数量的两倍，如果用每条内存引用未命中而不是每条指令未命中来衡量，会人为地降低未命中率。缺点是每条指令的未命中与架构有关；例如，每条指令的平均内存访问次数对于80x86和RISC V来说可能是非常不同的。因此，每条指令未命中率在只为某一种计算机架构进行设计的架构师中最为受欢迎，尽管RISC架构的相似性允许人们对其他架构提出见解。

示例：为了显示这两个未命中率公式之间的等价性，让我们重做前面的例子，这次假设每1000条指令的未命中数量为30。以指令数计算的内存滞留时间是多少？

答案：重新计算内存滞留周期：

![](https://3404518874-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdXvFTNpUgIWBzBBchio%2Fuploads%2FMA5rCC88J4oMCfFrFUkj%2FNeatReader-1656576186629.png?alt=media\&token=d90f78f6-679f-4d84-8222-a8a91f1c7666)

我们得到的答案与上一个示例相同，显示出两个方程的等效性。
