开发线程级并行性(Exploiting Thread-Level Parallelism)

前面描述的技术依赖于程序中可用的并行性来加速执行。除此之外,CPU 还支持一些技术来开发在 CPU 上执行的进程和/或线程之间的并行性。接下来,我们将讨论三种开发线程级并行性(TLP,Thread-Level Parallelism)的技术:多核系统(multicore systems)、同时多线程(simultaneous multithreading)和混合架构(hybrid architectures)。这些技术使得从可用硬件资源中榨取最大效率成为可能,并提高系统的吞吐量。

多核系统(Multicore Systems)

随着处理器架构师开始触及半导体设计和制造的实际限制,GHz 竞赛放缓,设计师不得不专注于其他创新来提升 CPU 性能。其中一个关键方向是多核设计,试图在每一代中增加核心数量。其思想是在单个芯片上复制多个处理器核心,让它们同时服务于不同的程序。例如,一个核心可以运行网络浏览器,另一个核心可以渲染视频,还有一个可以播放音乐,所有这些同时进行。对于服务器机器,来自不同客户端的请求可以在不同的核心上处理,这可以大大增加此类系统的吞吐量。

第一款面向消费者的双核处理器是 2005 年发布的 Intel Core 2 Duo,随后是同年晚些时候发布的 AMD Athlon X2 架构。多核系统导致许多软件组件需要重新设计,也影响了我们编写代码的方式。如今,面向消费者的设备中几乎所有处理器都是多核 CPU。在撰写本书时,高端笔记本电脑包含十多个物理核心,服务器处理器在单个插槽上包含超过 100 个核心。

这听起来很令人印象深刻,但我们不能无限制地增加核心。首先,每个核心工作时都会产生热量,从核心通过处理器封装安全散热仍然是一个挑战。这意味着当更多核心在运行时,热量可能很快超过散热能力。在这种情况下,多核处理器将降低时钟速度。这是为什么具有大量核心的服务器芯片的频率远低于笔记本电脑和台式机处理器的原因之一。

多核系统中的核心通过互连(interconnect)相互连接,也与末级缓存(last-level cache)和内存控制器等共享资源连接,该互连通常具有环形或网状拓扑结构。CPU 设计师面临的另一个挑战是在核心数量增长时保持机器的平衡。当你复制核心时,某些资源保持共享,例如内存总线和末级缓存。这导致随着核心数量增加,性能回报递减,除非你同时解决其他共享资源的吞吐量问题,例如互连带宽、末级缓存大小和带宽以及内存带宽。共享资源经常成为多核系统中性能问题的来源。

同时多线程(Simultaneous Multithreading)

改善多线程性能的一种更复杂的方法是同时多线程(SMT,Simultaneous Multithreading)。人们经常使用术语超线程(Hyperthreading)来描述同样的事情。该技术的目标是充分利用 CPU 流水线的可用宽度。SMT 允许多个软件线程使用共享资源在同一物理核心上同时运行。更准确地说,来自多个软件线程的指令在同一周期内并发执行。这些线程不必来自同一进程;它们可以是恰好被调度到同一物理核心上的完全不同的程序。

图 SMT 展示了在非 SMT 和 2 路 SMT(SMT2)处理器上执行的示例。在两种情况下,处理器流水线的宽度均为 4,每个槽表示发射一条新指令的机会。100% 机器利用率是指没有未使用的槽,这在实际工作负载中从未发生。很容易看出,对于非 SMT 情况,有许多未使用的槽,因此可用资源没有得到充分利用。这可能由于各种原因发生。例如,在第 3 个周期,线程 1 无法继续推进,因为所有指令都在等待其输入变为可用。非 SMT 处理器会简单地停顿,而支持 SMT 的处理器会利用这个机会从另一个线程调度有用的工作。这里的目标是用另一个线程占用未使用的槽,以提高硬件利用率和多线程性能。

在 4 路非 SMT 和 4 路 SMT2 处理器上的执行。

在 4 路非 SMT 和 4 路 SMT2 处理器上的执行。

在 SMT2 实现中,每个物理核心由两个逻辑核心表示,操作系统可以将它们视为两个独立的处理器来执行工作。考虑这样一种情况:我们有 16 个软件线程准备运行,只有 8 个物理核心。在非 SMT 系统中,同时只有 8 个线程运行,而使用 SMT2,我们可以同时执行所有 16 个线程。在另一个假设情况下,如果两个程序在启用 SMT 的核心上运行,每个程序始终只利用四个可用槽中的两个,那么它们很有可能以与单独运行在该物理核心上相同的速度运行。

虽然两个程序在同一处理器核心上运行,但它们彼此完全隔离。在支持 SMT 的处理器中,即使指令混合在一起,它们也有不同的上下文,这有助于保证执行的正确性。为了支持 SMT,CPU 必须复制架构状态(程序计数器、寄存器)以维护线程上下文。其他 CPU 资源可以共享。在典型实现中,缓存资源在硬件线程之间动态共享。用于跟踪 OOO 和推测执行的资源可以复制或划分。

在 SMT2 核心中,两个逻辑核心真正同时运行。在 CPU 前端(Frontend),它们交替取指(每个周期或几个周期交替一次)。在后端(Backend),处理器每个周期从所有线程中选择指令执行。随着处理器在两个线程之间动态调度执行单元,指令执行是混合的。

因此,SMT 是一种非常灵活的设置,可以回收未使用的 CPU 发射槽。SMT 在提供多线程优势的同时,提供了同等的单线程性能。选择支持 SMT 的现代 CPU 通常实现 2 路(SMT2),有时是 4 路(SMT4)SMT。

SMT 也有其自身的缺点。由于某些资源在逻辑核心之间共享,它们可能最终竞争使用这些资源。最可能的 SMT 惩罚是由对 L1 和 L2 缓存的竞争引起的。由于它们在两个逻辑核心之间共享,缓存空间可能不足,并强制驱逐另一个线程将来会使用的数据。

SMT 给软件开发者带来了相当大的负担,因为它使预测和测量在 SMT 核心上运行的应用程序的性能变得更加困难。想象一下,你正在 SMT 核心上运行性能关键代码,突然操作系统将另一个要求苛刻的作业放在同级逻辑核心上。你的代码几乎耗尽了机器的资源,现在你需要与其他人共享它们。这个问题在云环境中尤为突出,在那里你无法预测你的应用程序是否会有"嘈杂的邻居"。

某些同时多线程实现也存在安全问题。研究人员表明,一些早期实现存在一个漏洞,通过该漏洞,一个应用程序可以通过监控其缓存使用情况,从在同一处理器同级逻辑核心上运行的另一个应用程序那里窃取关键信息(如加密密钥)。由于硬件安全超出了本书的范围,我们不会深入讨论这个话题。

混合架构(Hybrid Architectures)

计算机架构师还开发了一种混合 CPU 设计,其中在同一处理器中放置了两种(或多种)类型的核心。通常,更强大的核心与相对较慢的核心相结合,以解决不同的目标。在这样的系统中,大核心用于延迟敏感任务,小核心提供更低的功耗。此外,两种类型的核心可以同时利用,以提高多线程性能。所有核心都可以访问相同的内存,因此工作负载可以即时从大核心迁移到小核心,反之亦然。其目的是创建一种能够更好地适应动态计算需求并使用更少功耗的多核处理器。例如,视频游戏既有单线程突发执行的部分,也有可以扩展到多核的工作部分。

第一个主流混合架构是 Arm 的 big.LITTLE,于 2011 年 10 月推出。其他供应商也采用了这种方法。苹果于 2020 年推出了其 M1 芯片,该芯片具有四个高性能"Firestorm"核心和四个节能"Icestorm"核心。Intel 于 2021 年推出了其 Alder Lake 混合架构,在顶级配置中具有八个 P 核和八个 E 核。

混合架构结合了两种核心类型的最佳特性,但它们也面临各自的挑战。首先,它要求核心完全 ISA 兼容,即它们应该能够执行同一组指令。否则,调度会受到限制。例如,如果大核心具有小核心上不可用的一些高级指令,那么你只能将大核心分配给使用此类指令的工作负载。这就是为什么通常供应商在为混合处理器选择 ISA 时采用"最大公约数"方法。

即使核心 ISA 兼容,调度也变得具有挑战性。不同类型的工作负载需要特定的调度方案,例如突发执行与稳定执行、低 IPC 与高 IPC1、低重要性与高重要性等。这很快就变得非常复杂。以下是最优调度的一些考虑因素:

  • 利用小核心节省功耗。不要为后台工作唤醒大核心。
  • 识别适合卸载到小核心的候选任务(低重要性、低 IPC)。类似地,将高重要性、高 IPC 任务提升到大核心。
  • 分配新任务时,首先使用空闲的大核心。对于 SMT 情况,使用两个逻辑线程都空闲的大核心。之后使用空闲的小核心。再之后使用大核心的同级逻辑线程。

从程序员的角度来看,不需要更改代码即可使用混合系统。这种方法在面向客户端的设备中变得非常流行,尤其是在智能手机中。

1. IPC - 每周期指令数(Instructions Per Cycle),参见 [IPC]。

results matching ""

    No results matching ""