在传统的多核处理器中,例如以前 Mac 机型中使用的 Intel CPU,所有内核都是相同的。因此,将线程分配给核心是平衡其负载的问题,即所谓的对称多处理(SMP)。

coremanintel

在 Activity Monitor 的 CPU History 窗口中,核心负载(以 CPU % 表示)按时间显示,最旧的值显示在左侧。左半部分奇数核心为实数,显示重负载下 8 核 Intel Xeon W 中的 8 个核心。右半部分的偶数核心是超线程的虚拟核心,用于应对最重的负载。

Apple Silicon 芯片中的 CPU 是不同的,因为它们包含两种不同的内核类型,一种是为高性能而设计的(Performance、P 或 Firestorm 内核),另一种是为提高能效而设计的(Efficiency、E 或 Icestorm 内核)。为了使这些工作正常,线程需要按核心类型分配,该任务可以留给应用程序和进程,就像在Asahi Linux 中一样,或者由操作系统管理,就像在 macOS 中一样。本文解释了 macOS 如何管理所有 Apple M1 系列芯片中的核心分配,即所谓的非对称多处理(AMP,尽管其他人更喜欢称之为异构计算)。

体系结构

M1系列芯片有两种CPU内核:

  • E 核包含大约一半的 P 核内部处理单元,最高频率为 2064 MHz。
  • P 内核的最大频率更高,原始 M1 为 3204 MHz,M1 Pro/Max/Ultra 为 3228 MHz。

M1 系列芯片提供三种 CPU 内核配置:

  • 在 MacBook Air、13 英寸 MacBook Pro、iMac 和 Mac mini 中配备 4 个 E 和 4 个 P 核心的原始 M1
  • M1 Pro 和 Max,配备 2 个 E 和 8 个 P 核心,用于 14 英寸和 16 英寸 MacBook Pro 以及 Mac Studio Max;
  • Mac Studio Ultra 中的 M1 Ultra,具有 4 个 E 和 16 个 P 内核。

一些 MacBook Pro 14 英寸笔记本电脑的 M1 Pro 芯片减少了,只有 6 个 P 内核,而不是 8 个。

为了简化内核的管理,macOS 在功能上将它们划分为 2-4 个相同类型的内核组成的集群。不幸的是,系统级别的核心编号(如 等工具所示powermetrics)和 Activity Monitor 中显示的不同。为了与后者保持一致,我这里沿用其核心编号,但按照系统编号集群。从 macOS Monterey 12.3.1 开始,这三款芯片具有以下功能集群:

  • M1各有一个集群E0和P0,每个集群包含4个同类型的内核;
  • M1 Pro 和 Max 具有 2 个 E 核心 (E0) 的一个集群,以及每个包含 4 个 P 核心 (P0、P1) 的两个集群;
  • M1 Ultra 有一个包含 4 个 E 核心 (E0) 的集群,以及四个包含 4 个 P 核心 (P0、P1、P2、P3) 的集群。

任何给定集群中的所有核心都以相同的频率运行,并且通常(但不总是)在集群内实现负载平衡。有时负载分布更不均匀,在特殊情况下,某些线程可能只分配给集群中的一个核心。

线程控制

与 Asahi Linux 不同,macOS 不提供对内核、内核类型或集群的直接访问,至少在公共 API 中不提供。相反,这些通常通过 Grand Central Dispatch 使用服务质量 (QoS) 设置进行管理,然后 macOS 使用该设置来确定线程管理策略。

QoS 最低的线程只会在 E 集群上运行,而 QoS 较高的线程可以分配给 E 或 P 集群。后一种行为可以通过taskpolicy命令工具或setpriority()代码中的函数动态修改。这些可以将更高 QoS 的线程限制为仅在 E 核或 E 或 P 核上执行。但是,它们无法更改仅在 E 集群上执行最低 QoS 线程的规则。

macOS 本身采用了一种策略,即大多数(如果不是全部)后台任务都以最低的 QoS 运行。其中包括自动 Time Machine 备份和 Spotlight 索引维护。这也适用于存档实用程序执行的压缩和解压缩:例如,如果您下载 xip 格式的 Xcode 副本,则解压缩需要很长时间,因为大部分代码都被限制在 E 内核中,并且无法更改那。

后台线程

最低 QoS 线程在原始 M1M1 Pro/Max 芯片中的加载和运行方式不同,因为它们具有不同的 E 集群大小。

在原始的 M1 芯片中,具有 4 个 E 内核,运行 QoS 9 线程,内核频率设置在 1000 MHz(1 GHz)左右。拥有 2 个 E 核心的 M1 Pro/Max 的情况不同:如果只有一个线程,它会在集群上以大约 1000 MHz 的频率运行,但如果有两个或更多线程,则频率会增加到 2064兆赫。这确保了 M1 Pro/Max 中的 E 集群至少提供与原始 M1 中的后台任务相同的性能,尽管集群大小不同,但功耗相似。

常见的例外是进程的最低 QoS 线程,例如backupd,它们也经历 I/O 节流,并且在 M1 Pro/Max 上以大约 1000 MHz 的频率运行。

用户线程

QoS 高于 9 的所有线程都以类似方式处理,不同之处在于赋予其队列的优先级。

由于高 QoS 线程有资格在任一核心类型和任何核心集群上运行,它们的管理M1M1 Pro/Max 变体之间有所不同。在原始 M1 上,具有单个 P 集群,最多 8 个线程的批次可以分配到两个可用集群,每个集群有 4 个线程槽可用。当有 4 个或更少的线程时,它们将尽可能运行在 P 集群上,而 E 集群仅在队列中有更多高 QoS 线程时才被招募。P 核心以大约 3 GHz 的频率运行,E 核心以大约 2 GHz 的频率运行,是 QoS 9 线程通常使用的频率的两倍。

M1 Pro 和 Max 芯片总共有 3 个集群,每个 4 个 P 核中的 2 个,加上半尺寸的 2 核 E 集群。队列中最多有4个线程,它们将被分配到第一个P簇(P0);线程 5-8 将转到第二个 P 集群 (P1),否则它将保持卸载和非活动状态以节省成本。如果队列中还有另外 2 个线程,它们将在 E 核上运行。频率设置是内核类型的最大值,在 P0 和 P1 上设置为 3228 MHz,在 E0 上设置为 2064 MHz。

M1 Ultra 芯片共有五个集群,每个集群有 4 个内核。它们遵循与 M1 Pro/Max 芯片相同的策略,但在使用 E0 之前加载了所有 4 P 集群。

但是,有两种情况下代码似乎只在单个内核上运行:在引导过程中,在内核初始化并运行其他内核之前,代码仅在单个活动的 E 内核上运行。另一种情况是在开始安装过程之前“准备”下载的 macOS 更新。在 M1 Pro/Max 芯片上,5 个线程被赋予一个核心价值的活动驻留,表示为 100% CPU,但仅限于单个 P 核心,即 2 P 集群中的第一个(P0,标记如下作为核心 3)。

奇核02

在准备安装更新的 30 分钟内,这种不寻常的活动驻留分布会持续存在。

负载下的图案

以下更典型的例子展示了 macOS 策略的效果,这些例子取自 Activity Monitor 的 CPU History 窗口。

coremanm1

这个原始的 M1 芯片在这里受到了来自越来越多的 CPU 密集型线程的一系列负载。它的 2 个集群 E0 和 P0 以蓝色框区分。1-4 个高 QoS 线程(从左至右),负载完全由 P0 集群承担,然后 5-8 个线程由 E0 集群分担。

coremanm1pro

这款 M1 Pro 芯片承受着来自许多线程的繁重且不断变化的负载,其中一些处于后台 QoS,而另一些处于更高的 QoS。虽然大部分负载由 E0 集群中的 2 个核心承担,但 P0 也在大部分时间被加载,并且 P1 被招募来承担一些峰值。

coremanultra

我已经从 M1 Ultra 重新排列了本示例中显示的内核,以将它们分成各自的集群,E0 在顶部,P0 到 P3 在下面的两列中。这里显示的负载是登录后最初几分钟内的典型负载,E0 和 P0 上的负载很重,在早期峰值期间溢出到 P1-3。

Activity Monitor 尚未(尚未)提供的有关 M1 内核的一项重要信息是集群频率。以低于 1000 MHz 的频率以 100% CPU(相当于活动驻留)运行的集群完成指令的速率不到 100% CPU 和频率为 2064 MHz 的同一集群的一半。不幸的是,目前获取频率信息的唯一可用方法是命令工具powermetrics

下图给出了原始 M1、M1 Pro 和 Max 芯片中 CPU 内核的 macOS 管理摘要。当我完成有关 M1 Ultra 的信息时,我将在下一个版本中包含这些信息。如果您拥有 M1 Ultra,熟悉powermetrics并愿意提供帮助,我将很高兴与您合作。

调度线程M1

由于 Apple 预计将在 6 月初的下一届 WWDC 上宣布其 M1 系列的继任者,因此看到其核心架构和 macOS 提供的管理它的策略将会很有趣。