# 什么是流水线?
流水线是一种通过将指令的执行过程分解为多个子过程,并让这些子过程同时执行,从而实现并行加速的技术。这使得 CPU 的每条指令在执行过程中可以与其他指令的不同子过程同时进行,显著提高了指令的吞吐率。
通过流水线技术,可以将 CPI (每条指令的平均时钟周期数) 降低到接近 1,并将时钟周期降低到单周期处理器的 1/5,从而达到时空复用和并行加速的目的。
CPU 执行时间的计算公式为:
通过优化这个公式中的各个参数,可以提升 CPU 的整体性能。
# CPU 性能优化方法
除了流水线,还有多种技术可以用来优化 CPU 的执行时间,它们主要分为:指令级并行、线程级并行和异构计算。
# 指令级并行
# 多周期
通过多周期设计,可以降低 CPI 和时钟周期的乘积,但效率不如流水线。
# 多发射
多发射技术使得 CPU 可以在每个时钟周期内取址并执行多条指令,从而将 CPI 降低到小于 1。它主要有两种实现方式:
- 超标量 (Superscalar):这是一种动态指令调度技术。处理器设置多个独立的功能部件,并通过硬件动态调度,确保在同一时钟周期内执行多条指令。
- 超长指令字 (VLIW):这是一种静态多发射技术。在一条指令中设置多个独立的字段,每个字段可以分别独立地控制各个功能部件并行工作,指令的并行性由编译器在编译时决定。
# 超级流水线 (Superpipelining)
通过增加流水线深度,进一步缩短每个流水线阶段的时钟周期,从而降低整体的时钟周期。
# 线程级并行
# 超线程 (Hyper-Threading)
当一个线程因为等待访存等原因被阻塞时,处理器可以执行另一个线程的任务。这是一种利用 CPU 空闲时间提高利用率的有效策略,可以采用贪心调度策略来优化任务分配。
# 多核 (Multi-Core)
将多个独立的处理器核心集成在一个芯片上,实现真正的并行处理。在多核系统中,并行加速的性能提升遵循阿姆达尔定律,即随着核数的增加,边际收益会逐渐降低。
- 并行加速比 () 的计算公式为:
其中, 为程序中可并行部分的比例, 为并行部分的加速比(通常可以理解为核数)。
在多核系统中,核心之间的数据共享和同步是需要重点考虑的问题。
# 异构计算
异构计算的核心思想是根据计算任务的特性,将其分配到最适合的硬件上执行,从而最大化系统性能。
- GPU (图形处理器):GPU 是一种专门为大规模并行计算设计的硬件,其架构采用 SIMD (单指令流多数据流) 的设计理念,非常适合处理大规模、重复的并行任务。因此,在异构计算中,串行复杂的任务通常由 CPU 处理,而并行简单的任务则由 GPU 处理。
- TPU (张量处理器):TPU 是面向特定领域(如机器学习)的专用处理器。
# 流水线的基本概念
在深入理解流水线的性能之前,需要掌握几个基本概念:
- 延时与吞吐率:延时指的是完成一条指令所需要的总时间,而吞吐率指的是单位时间内完成的指令数。它们通常用周期数和时钟周期来衡量。
- 调度:指的是将操作分配到特定的时钟周期中执行。
- 资源绑定:指的是将操作分配到特定的硬件资源上执行,例如寄存器、运算单元等。
资源共享方式主要有两种:
- 时分复用:在不同时间段内重复使用同一个硬件资源。
- 空分复用:在同一时间段内使用多个硬件资源。
# 流水线的性能指标
对于一个包含 条指令、 级流水线、级间延时为 的系统,其性能指标可以从以下几个方面来衡量:
- 实际吞吐率 ():
- 最大吞吐率 ( ):当指令数量趋近于无穷时,流水线的最大吞吐率。
- 加速比 ():不使用流水线所用的时间与使用流水线所用的时间的比值。
- 实际加速比 ():
- 最大加速比 ( ):当指令数量趋近于无穷时,流水线的最大加速比。
# 为什么无法达到最大加速比?
尽管理论上的最大加速比为 ,但在实际应用中,很难完全实现,主要有以下几个原因:
- 启动开销:各级流水线需要依次启动,无法直接达到 倍的加速效果。
- 流水线级长不均:流水线各级所需的工作时间可能不同,最终的时钟周期由最长的那一级决定,即 。
- 寄存器延时:流水线级间寄存器会引入额外的延时。
# 流水线中的“冒险”
“冒险”指的是由于指令之间存在依赖关系,导致流水线无法正常工作的情况,主要分为三种类型:
# 结构冒险 (Structural Hazards)
当两条指令需要同时使用同一个硬件资源时,就会发生结构冒险。
- 解决方法:
- 增加硬件资源,如增加独立的存储器端口。
- 调整指令占用资源的时间,例如将寄存器读写、RF 总线、存储器、ALU 等功能部件分离。
# 数据关联冒险 (Data Hazards)
当一条指令的执行依赖于流水线中正在运行的其他指令的结果时,就会发生数据关联冒险。
- 解决方法:
- Stall:暂停流水线,等待依赖的数据就绪。
- Forwarding:将前一条指令的执行结果直接通过旁路(Bypass)传递给后一条指令,而无需等待其写回寄存器。
- 编译器优化:通过重新排列指令来消除或减少数据依赖。
# 控制冒险 (Control Hazards)
当取指令的地址 (PC) 依赖于流水线中某个分支指令的执行结果时,就会发生控制冒险。
- 解决方法:
- Stall:暂停流水线,等待分支结果确定。
- Forwarding:将分支结果提前传递给取指单元。
- 延迟槽 (Delayed Slot):在分支指令后插入一条不受分支结果影响的指令,使其得以执行。
- 分支预测 (Branch Prediction):预测分支的结果,提前取指并执行,如果预测错误,则回滚并重新执行。
# 流水线的异常处理
当流水线中发生异常时,系统需要采取一系列动作来正确处理,确保程序状态的完整性和正确性。
- 记录异常地址:必须记录产生异常的指令的地址,以便后续处理。
- 保存和恢复状态:必须保存当前用户程序的状态(如寄存器值),并在异常处理结束后恢复,以便控制权可以交还给用户程序。
- 控制权转移:异常处理结束后,将控制权交还给用户程序,使其能够从中断处恢复执行。