读CSAPP之PIPE原理和实现

今年是离开校园的第六年,这六年来我一直在从事应用软件的设计、开发工作,大部分时间是在与高级编程语言、设计模式、业务逻辑打交道。它们大多流于表面,久而久之,与技术底层疏远了,诸如计算机组成原理、汇编语言、编译原理、数据结构以及算法慢慢得生疏,时至今日,向上碰到天花板,向下触到花岗岩。五年是一个契机,趁着下一个五年开始之际,我计划用三个月至半年时想间,重新学习这些知识,以期达到巩固基础,厚积薄发的目的。

本篇是我阅读《Computer System: A Programmer’s Perspective》一书的笔记,该书和与之搭配的《Professional Assembly Language》是我当下阅读计划的一部分。

我曾经不只三次翻阅第四章–处理器体系机构,然而至今未能完整读懂,其中的电子学和布尔代数小节,使我深深地为当年的逃课行为感到懊恼,–为此,我特意找了《数字电路简明教程》来补上这一课。下面这些文字,是我在半懂不懂的情况下,“断章取意”摘录的,只做自己的读书笔记。

Pipelining 原理

流水线化的目的就是每个时钟周期都发射一条指令,也就是说每个时钟周期都有一条新指令进入执行阶段,同时有一条旧指令退出执行流程。

流水线化会增加吞吐量(throughout),但同时也会增加一条指令从头到尾执行的全部时间,即延迟(latency),–延迟增加的原因是,多出来的流水线寄存器的时间开销。

流水线阶段之间的指令转移由时钟信号控制,并且时钟信号控制在每隔特定的时间间隔加载流水线寄存器。流水线寄存器采用时钟寄存器,其由时钟信号控制加载输入值,信号转播到流水线寄存器的输入,直到时钟上升时,才会改变寄存器的状态。用来保存程序计数器(PC)、条件代码(CC)和程序状态(Stat)等。

流水线的两个局限性表现为:

  1. 如果流水线划分的阶段不一致,那么系统吞吐量将受到最慢阶段的速度所限制。
  2. 如果流水线划分阶段过多,则会增加寄存器的时间开销,收益反而会下降。

相邻指令之间可能存在数据相关(data dependency)或者控制相关(control dependency),这时需要反馈路径来解决。

Pipelining 实现

将 SEQ 改造成 PIPE,需要完成两个修改:将 PC 的计算挪到取值阶段;在各个阶段之间加上流水线寄存器。PIPE 中没有硬件寄存器来存放程序计数器,而是根据从前一条指令保存下来的一些状态信息动态地计算 PC。这种对状态元素的改变称为重定时(retiming),其改变一个系统的状态表示,但并不改变它的逻辑行为,通常用来平衡一个系统中各个阶段之间的延迟。

流水线取出的指令如果是条件分支指令,或者是 ret,则需要等到几个时钟周期之后,才能确定选择分支,或者返回地址。分支预测就是猜测分支方向并根据猜测开始取值。

数据相关,下一条指令会用到这一条指令计算出的结果;控制相关,一条指令要确定下一条指令的位置,例如在执行跳转、调用或返回指令时。这些相关可能会导致流水线产生计算错误,称为冒险(hazard),可分为两类:数据冒险(data hazard)和控制冒险(control hazard)。

暂停(stalling)是避免冒险的一种常用技术,暂停时,处理器会停止流水线中一条或多条指令,直到冒险条件不再满足,但它会允许其他指令继续通过流水线。每次要把一条指令阻塞在译码阶段,就在执行阶段插入一个“气泡”,其就像一个自动产生的 nop 指令,不改变寄存器、存储器、条件码或程序状态。

Leave a comment

Your comment