4.5.9 性能分析

4.5.9 性能分析

我们可以看到,所有需要流水线控制逻辑进行特殊处理的条件,都会导致流水线不能够实现每个时钟周期发射一条新指令的目标。我们可以通过确定往流水线中插入气泡的频率,来衡量这种效率的损失,因为插入气泡会导致未使用的流水线周期。一条返回指令会产生三个气泡,一个加载/使用冒险会产生一个,而一个预测错误的分支会产生两个。我们可以通过计算 PIPE 执行一条指令所需要的平均时钟周期数的估计值,来量化这些处罚对整体性能的影响,这种衡量方法称为 CPI(Cycles Per Instruction,每指令周期数)。这种衡量值是流水线平均吞吐量的倒数,不过时间单位是时钟周期,而不是微微秒。这是一个设计体系结构效率的很有用的衡量标准。

如果我们忽略异常带来的性能损失(异常的定义表明它是很少出现的),另一种思考 CPI 的方法是,假设我们在处理器上运行某个基准程序,并观察执行阶段的运行。每个周期,执行阶段要么会处理一条指令,然后这条指令继续通过剩下的阶段,直到完成;要么会处理一个由于三种特殊情况之一而插入的气泡。如果这个阶段一共处理了 Ci 条指令和 Cb 个气泡,那么处理器总共需要大约 Ci + Cb 个时钟周期来执行 Ci 条指令。我们说“大约”是因为忽略了启动指令通过流水线的周期。于是,可以用如下方法来计算这个基准程序的 CPI:

CPI = (Ci + Cb) / Ci = 1.0 + Cb / Ci

也就是说,CPI 等于 1.0 加上一个处罚项 Cb / Ci,这个项表明执行一条指令平均要插入多少个气泡。因为只有三种指令类型会导致插入气泡,我们可以将这个处罚项分解成三个部分:

CPI = 1.0 + lp + mp + rp

这里,lp(load penalty,加载处罚)是当由于加载/使用冒险造成暂停时插入气泡的平均数,mp(mispredicted branch penalty,预测错误分支处罚)是当由于预测错误取消指令时插入气泡的平均数,而 rp(return penalty,返回处罚)是当由于 ret 指令造成暂停时插入气泡的平均数。每种处罚都是由该种原因引起的插入气泡的总数(Cb 的一部分)除以执行指令的总数(Ci)。

为了估计每种处罚,我们需要知道相关指令(加载、条件转移和返回)的出现频率,以及对每种指令特殊情况出现的频率。对 CPI 的计算,我们使用下面这组频率(等同于 [44] 和 [46] 中报告的测量值):

  • 加载指令(mrmovqpopq)占所有执行指令的 25%。其中 20% 会导致加载/使用冒险。
  • 条件分支指令占所有执行指令的 20%。其中 60% 会选择分支,而 40% 不选择分支。
  • 返回指令占所有执行指令的 2%。

因此,我们可以估计每种处罚,它是指令类型频率、条件出现频率和当条件出现时插入气泡数的乘积:

原因 名称 指令频率 条件频率 气泡 乘积
加载/使用 lp 0.25 0.20 1 0.05
预测错误 mp 0.20 0.40 2 0.16
返回 rp 0.02 1.00 3 0.06
总处罚 0.27

三种处罚的总和是 0.27,所以得到 CPI 为 1.27。

我们的目标是设计一个每个周期发射一条指令的流水线,也就是 CPI 为 1.0。虽然没有完全达到目标,但是整体性能已经很不错了。我们还能看到,要想进一步降低 CPI,就应该集中注意力预测错误的分支。它们占到了整个处罚 0.27 中的 0.16,因为条件转移非常常见,我们的预测策略又经常出错,而每次预测错误都要取消两条指令。

练习题 4.43 假设我们使用了一种成功率可以达到 65% 的分支预测策略,例如后向分支选择、前向分支就不选择(BTFNT),如 4.5.4 节中描述的那样。那么对 CPI 有什么样的影响呢?假设其他所有频率都不变。

练习题 4.44 让我们来分析你为练习题 4.4 和练习题 4.5 写的程序中使用条件数据传送和条件控制转移的相对性能。假设用这些程序计算一个非常长的数组的绝对值的和,所以整体性能主要是由内循环所需要的周期数决定的。假设跳转指令预测为选择分支,而大约 50% 的数组值为正。

A. 平均来说,这两个程序的内循环中执行了多少条指令?

B. 平均来说,这两个程序的内循环中插入了多少个气泡?

C. 对这两个程序来说,每个数组元素平均需要多少个时钟周期?