什么是CPU亲和力?何时使用?

CPU 亲和力(CPU affinity)是指在多处理器系统中,将一个进程或线程绑定到特定的CPU核心或处理器上,以限制其在系统中运行的位置。这样的做法可以带来一些性能上的优势,但也需要小心使用,因为不当的亲和性设置可能导致系统负载不均衡。

这是一种允许用户将进程或线程分配(或固定)到特定计算资源或计算资源组的技术。默认情况下,操作系统会使用复杂的启发式方法在所有可用内核之间调度进程,以确保运行时间的公平分配。

然而,有时候将特定的任务绑定到特定的核心上可能是有益的。采用 CPU 亲和力可以将选定的线程固定到指定的内核列表中,从而规避内核之间调度决策过程。

一些用例和优势包括:

  • 缓存局部性: 将进程或线程固定在一个CPU核心上,可以提高其在该核心的缓存命中率,因为该核心的缓存可能包含与该任务相关的数据。
  • 减少上下文切换: 避免进程或线程在不同核心之间切换可能减少上下文切换的开销。
  • 可预测性: 在实时系统中,通过将任务绑定到特定的核心上,可以提高任务的响应时间的可预测性。

在Python中,可以使用psutil库来设置进程的 CPU 亲和性。以下是一个简单的示例:

import psutil

# 获取当前进程的PID
pid = os.getpid()

# 获取系统的 CPU 数量
num_cpus = psutil.cpu_count()

# 将当前进程绑定到第一个CPU核心上
psutil.Process(pid).cpu_affinity([0])

print(f"进程 {pid} 已绑定到第一个CPU核心上。")

请注意,对于一般的应用程序,不必手动设置 CPU 亲和性,因为操作系统通常能够有效地管理进程和线程的分配。手动设置 CPU 亲和性通常是在特殊情况下,例如对性能要求非常高或对实时性要求较高的系统中。

CPU 亲和性的好处
对于一般用途的计算机来说,公平是件好事。但当我们想要获得最佳性能时,我们不需要操作系统突然将我们的线程从其内核上抢走,然后又将其重新安排到完全不同的内核上,从而毁掉了它实现有效缓存利用的任何机会。

不,我们不需要公平。我们不想在俱乐部排队等候。

这就是 CPU Affinity 的好处。没有漫长的运行排队时间。不会因为重新安排而导致核心缓存崩溃,从而迫使我们不得不经常重新访问 LLC 或 RAM。

说到多线程应用,当我们将线程固定在相邻内核上时,就能确保线程间通信的低延迟。但是,如果我们真正理解了现代 CPU 的内核邻接性,那么这种延迟还能降低多少呢?

微架构的演变
整体芯片的日子已经屈指可数了。进入小芯片时代,其中包含核心子集和缓存的较小芯片与单个 CPU 上的其他此类芯片互连。

  • 英特尔尽可能地坚持整体芯片。但随着摩尔定律的放缓和 Dennard Scaling 的崩溃,英特尔最终屈服于采用嵌入式多芯片互连桥接(EMIB)互连技术,用于更高核心数量的 Sapphire Rapids 变体。
  • AMD 很早就通过其 Infinity Fabric 互连加入了小芯片运动,多个核心[url=https://jprahman.substack.com/p/amd-cpu-topology-rome-milan]复合[/url][url=https://jprahman.substack.com/p/amd-cpu-topology-rome-milan]芯片[/url](CCD) [url=https://jprahman.substack.com/p/amd-cpu-topology-rome-milan]([/url]每个包含一个或多个核心复合体 (CCX))通过 IO 芯片 (IOD) 进行通信。

最近的通用芯片互连扩展(UCIe)开放规范,用于芯片互连,英特尔、AMD、ARM 和其他几个公司都属于该规范。这一标准为混合和匹配即插即用小芯片铺平了道路!

这种小芯片的势头将多插槽 NUMA 系统的延迟影响降低到单插槽的水平。
为什么?因为现代 CPU 上的小芯片本质上是它们自己的插槽,AMD 的 Infinity Fabric 和英特尔的 EMIB 充当插槽间连接(例如,HyperTransport 或 UPI)。

那么,这会如何影响我们执行 CPU Affinity 的方式呢?

微架构对 CPU 亲和力的影响
驻留在同一小芯片中的内核表现出比不同小芯片中的内核之间的通信延迟更低的通信延迟。选择几种可用的核心到核心延迟测量工具中的任何一种,并在您自己的 CPU 上运行它以进行证明。

总之:

  • 现代CPU的微架构演进导致同一芯片上的核心之间的通信延迟较低,因此在配置CPU Affinity时需要考虑核心的邻近性。
  • 配置CPU Affinity可以提高性能,避免线程在不同核心之间频繁切换,从而减少缓存利用的损失。
  • 观察到实际应用程序的性能提高了 5 - 10%,并且如果在更高核数的 CPU 上发现更大的改进。

如果您真正关心低延迟和/或高吞吐量,那么有一个您要做的事情的标准列表。其中包括:

  • 使用CPU Affinity绕过操作系统调度程序,实现每核线程配置
  • 通过利用应用程序线程之间的消息传递来避免直接数据共享及其所需的同步开销(即锁定)