苹果M1芯片为何如此快?
2020-12-18
来源:半导体行业观察
除了轻度办公以外,Arm 架构的苹果电脑还可以打游戏、看视频、跑深度学习,效率都还不错。
最近,很多人的 M1 芯片版苹果 MacBook 和 Mac Mini 到货了。在不少测试中,我们看到了令人期待的结果:M1 芯片跑分比肩高端 X86 处理器,对标的 CPU 是 Ryzen 4900HS 和英特尔 Core i9,还能跟英伟达的 GPU GTX 1050Ti 打得有来有回。5 纳米的芯片,真就如此神奇?
自苹果发布搭载自研 M1 芯片的 Mac 产品后,人们对 M1 芯片充满了好奇,各种测评层出不穷。最近,开发者 Erik Engheim 撰写长文,分析 M1 芯片速度快背后的技术原因,以及英特尔和 AMD 等芯片厂商的劣势。
关于苹果的 M1 芯片,这篇文章将围绕以下问题展开讨论:
M1 芯片速度快背后的技术原因。
苹果是不是采用了什么特殊的技术来实现这一点?
如果 Intel 和 AMD 也想这么做,难度有多大?
鉴于苹果的官方宣传中存在大量专业术语,我们先从最基础的讲起。
什么是 CPU?
在提到英特尔和 AMD 的芯片时,我们通常谈论的是中央处理器(CPU),或者叫微处理器。它们从内存中提取指令,然后按顺序执行每条指令。
一个基本的 RISC CPU(不是 M1)。指令沿着蓝色箭头从内存移动到指令寄存器。解码器用来解读指令的内容,同时通过红色的控制线来连通 CPU 的各个部分。ALU 对寄存器中的数字进行加减运算。
CPU 本质上是一个设备,包含许多被称为寄存器的内存单元和被称为算术逻辑单元(ALU)的计算单元。ALU 执行加法、减法和其他基础数学运算。然而,这些只与 CPU 寄存器相连。如果想把两个数字加起来,你必须把这两个数字从内存中取出,放到 CPU 的两个寄存器中。
下面是 RISC CPU(M1 中的 CPU 类型)执行的几个典型指令示例:
此处的 r1 和 r2 是寄存器。当前的 RISC CPU 无法对不在寄存器中的数字执行示例中的运算,比如不能将在两个不同位置的内存中的数字相加,而是要将两个数字各放到一个单独的寄存器里。
在上面的示例中,我们必须先将内存位置为 150 的数字放到寄存器 r1 中,然后将位置为 200 的数字放到 r2 中,只有这样,这两个数字才能依据 add r1, r2 指令进行加法运算。
这种老式机械计算器有两个寄存器:累加器和输入寄存器。现代 CPU 通常有十几个寄存器,而且它们是电子的,不是机械的。
寄存器的概念由来已久。例如,在上图的机械计算器上,寄存器用来存放加起来的数字。
M1 不是 CPU 这么简单
M1 芯片不是 CPU,而是把多个芯片集成到了一起,CPU 只是其中的一部分。
可以说,M1 是把整个计算机放在了一块芯片上。M1 包含 CPU、GPU、内存、输入 / 输出控制器,以及完整计算机所需的其他很多东西,这就是我们经常会在手机上看到的 SoC(片上系统)概念。
M1 是一个片上系统。也就是说,将构成一台计算机的所有部件都放在一块硅芯片上。
如今,如果你从英特尔或 AMD 购买一块芯片,你拿到的实际上是一个微处理器包,而过去的计算机主板上是多个单独的芯片。
计算机主板示例,上面包含内存、CPU、显卡、IO 控制器、网卡等部件。
然而,现在我们可以在一块硅片上集成大量晶体管,因此 AMD、英特尔等公司开始将多个微处理器放在一块芯片上。我们将这些芯片称为 CPU 核心。一个核心基本上是一个完全独立的芯片,它可以从内存中读取指令并执行计算。
具备多个 CPU 核心的微芯片。
很长一段时间以来,添加更多通用 CPU 核心成为提高芯片性能的主要方法,但有家厂商没这么做。
苹果的异构计算策略没那么神秘
在提升性能的道路上,苹果并没有选择增加更多通用 CPU 核心,而是采取了另一种策略:添加更多专用芯片来完成一些专门的任务。这样做的好处是:与通用 CPU 核心相比,专用芯片可以使用更少的电流执行任务,而且速度还更快。
这并不是什么全新的技术。多年来,图形处理单元(GPU)等专用芯片已经存在于英伟达和 AMD 的显卡中,执行与图形相关的操作,速度比通用 CPU 快得多。
苹果只是在这个方向上走得更加彻底。除了通用核心和内存之外,M1 包含了一系列专用芯片:
CPU(中央处理器):SoC 的「大脑」,运行操作系统和 app 的大部分代码。
GPU(图形处理器):处理图形相关的任务,如可视化 app 的用户界面和 2D/3D 游戏。
IPU(图像处理单元):用于加快图像处理应用所承担的常见任务。
DSP(数字信号处理器):具备比 CPU 更强的数学密集型功能,包括解压音乐文件。
NPU(神经网络处理器):用于高端智能手机,加速语音识别等机器学习任务。
视频编码器 / 解码器:以高能效的方式处理视频文件和格式的转换。
Secure Enclave:负责加密、身份验证,维护安全性。
统一内存(unified memory):允许 CPU、GPU 和其他核心快速交换信息。
这就是使用 M1 Mac 进行图像和视频编辑时速度有所提升的一部分原因。许多此类任务可以直接在专用硬件上运行,这样一来,相对廉价的 M1 Mac Mini 就能够轻松对大型视频文件进行编码。
你或许疑惑,统一内存与共享内存有什么区别。将视频内存与主存共享的做法导致了低性能,因为 CPU 和 GPU 必须轮流访问内存,共享意味着争用数据总线。
统一内存的情况就不同了。在统一内存中,CPU 和 GPU 可以同时访问内存,并且 CPU 和 GPU 还可以相互告知一些内存的位置。以前 CPU 必须将数据从其主存区域复制到 GPU 使用的区域,而使用统一内存模式,无需复制,通过告知内存位置即可令 GPU 使用该内存。
也就是说,M1 上各种专有协处理器都可以使用同一内存池彼此快速地交换信息,从而显著提升性能。
英特尔和 AMD 为什么不效仿这一策略?
其他 ARM 芯片制造商也越来越多地投入专用硬件。AMD 开始在某些芯片上安装功能更强大的 GPU,并通过加速处理器(APU)逐步向某种形式的 SoC 迈进。(APU 将 CPU 核心和 GPU 核心放置在同一芯片上。)
AMD Ryzen 加速处理器在同一块芯片上结合 CPU 和 GPU,但不包含其他协处理器、IO 控制器或统一内存。
英特尔和 AMD 不这么做是有重要原因的。SoC 本质上是芯片上的整个计算机,这使得它非常适合实际的计算机制造商,例如惠普、戴尔。计算机制造商可以简单地获取 ARM 知识产权许可,并购买其他芯片的 IP,来添加他们认为自己的 SoC 应该具备的任意专用硬件。然后,他们将已完成的设计交给半导体代工厂,比如 GlobalFoundries 和台积电(TSMC),台积电现在是 AMD 和苹果的芯片代工厂。
那么问题来了。英特尔和 AMD 的商业模型都是基于销售通用 CPU(只需将其插入大型 PC 主板)。计算机制造商只需从不同的供应商那里购买主板、内存、CPU 和显卡,然后将它们集成即可。但这种方式已经渐渐淡出。
在 SoC 时代,计算机制造商无需组装来自不同供应商的物理组件,而是组装来自不同供应商的 IP(知识产权)。他们从不同供应商那里购买显卡、CPU、调制解调器、IO 控制器等的设计,并将其用于设计 SoC,然后寻找代工厂完成制造过程。
但是英特尔、AMD、英伟达都不会将其知识产权给戴尔或惠普,让他们为自己的计算机制造 SoC。
当然,英特尔和 AMD 可能只是开始销售完整的 SoC,但是其中包含什么呢?PC 制造商可能对此有不同的想法。英特尔、AMD、微软和 PC 制造商之间可能就 SoC 要包含哪些专用芯片产生冲突,因为这些芯片需要软件支持。
而对于苹果来说,这很简单。苹果控制整个产品,比如为机器学习开发者提供如 Core ML 库等。至于 Core ML 是在苹果的 CPU 上运行还是 Neural Engine,这是开发者无需关心的实现细节了。
CPU 快速运行的根本挑战
异构计算只是一部分原因。M1 的快速通用 CPU 核心 Firestorm 确实速度非常快,与之前的 ARM CPU 核心相比,二者的速度差距非常大。与 AMD 和英特尔核心相比,ARM 也非常弱。相比之下,Firestorm 击败了大多数英特尔核心,也几乎击败了最快的 AMD Ryzen 核心。
在探讨 Firestorm 的速度成因之前,我们先来了解让 CPU 快速运行的核心意义。
原则上可以通过以下两种策略来完成 CPU 加速的任务:
以更快的速度顺序执行更多指令;
并行执行大量指令。
在上世纪 80 年代,这很容易做到。只要增加时钟频率,就能更快执行指令。每个时钟周期表示计算机执行某项任务的时间,但是这项任务可能非常微小。一条指令由多个较小的任务构成,因此可能需要多个时钟周期。
但是现在已经几乎不可能增加时钟频率了,所以第二个策略「并行执行大量指令」是目前研发的重心。
多核还是乱序处理器?
这个问题有两种解决方法。一种是引入更多 CPU 核心。从软件开发者的角度讲,这类似于添加线程,每个 CPU 核心就像一个硬件线程。双核 C+PU 可以同时执行两个单独的任务,即两个线程。这些任务可以被描述为两个存储在内存中的单独程序,或者同一个程序被执行了两次。每个线程需要记录,例如该线程当前在程序指令序列中的位置。每个线程都可以存储临时结果(应分开存储)。
原则上,处理器可以在只有一个核心的情况下运行多个线程。这时,处理器只能是暂停一个线程并存储当前进程,然后再切换到另一个线程,之后再切换回来。这并不能带来太多的性能提升,仅在线程经常悬停来等待用户输入或者慢速网络中的数据等时才使用。这些可以称为软件线程。硬件线程意味着可以使用实际的附加物理硬件(如附加核心)来加快处理速度。
问题在于开发者必须编写代码才能利用这一点,一些任务(例如服务器软件)很容易编写,你可以想象分别处理每个连接的用户。这些任务彼此独立,因此拥有大量核心是服务器(尤其是基于云的服务)的绝佳选择。
具有 128 个核心的 Ampere Altra Max ARM CPU 专为云计算而设计,大量硬件线程是一项优势。
这就是你会看到 128 核心 Ampere Altra Max ARM CPU 的原因了。该芯片专为云计算制造,不需要疯狂的单核性能,因为在云上需要每瓦具有尽可能多的线程来处理尽可能多的并发用户。
而苹果则不同,苹果生产单用户设备,大量线程并不是优势。苹果的设备多用于游戏、视频编辑、开发等。苹果希望台式机具有精美的响应图形和动画。
桌面软件通常不需要利用很多核心,例如,电脑游戏通常需要 8 个核心,在这种情况下 128 个核心就完全是浪费了。因此,用户需要的是更少但更强大的核心。
乱序执行是一种并行执行更多指令但不以多线程执行的方式。开发者无需专门编码其软件即可利用它。从开发者的角度来讲,每个核心的运行速度都更快了。
要了解其工作原理,首先我们需要了解一些内存知识。在一个特定的内存位置上请求数据的速度很慢。但是与获得 128 个字节相比,延迟获得 1 个字节的影响不大。数据通过数据总线发送,你可以将其视为内存与数据经过的 CPU 不同部分之间的一条通道或管道。实际上它只是一些可以导电的铜线。如果数据总线足够宽,你就可以同时获取多个字节。
因此 CPU 一次执行一整个指令块,但是这些指令被编写为一条接着一条执行。现代微处理器会进行「乱序执行」。这意味着它们能够快速分析指令缓冲区,查看指令之间的依赖关系。示例如下:
01: mul r1, r2, r3 // r1 ← r2 × r3
02: add r4, r1, 5 // r4 ← r1 + 5
03: add r6, r2, 1 // r6 ← r2 + 1
乘法是一个缓慢的运算过程,需要多个时钟周期来执行。第二条指令仅需等待,因为其计算取决于先知道放入 r1 寄存器的结果。但是,第三条指令并不取决于先前指令的计算结果,因此乱序处理器可以并行计算此指令。
但现实情况往往有数百条指令,CPU 能够找出这些指令之间的所有依赖关系。
它通过查看每个指令的输入来分析指令的输入是否取决于一或多个其他指令的输出,输入和输出指包含之前计算结果的寄存器。
例如上例中,add r4, r1, 5 指令依赖于来自 r1 的输入,而 r1 通过 mul r1, r2, r3 指令得到。
我们可以将这些关系链接在一起,形成 CPU 可以处理的详细图。图的节点表示指令,边表示连接它们的寄存器。CPU 可以分析这类节点图,并确定可以并行执行的指令,以及在继续执行之前需要在哪一步等待多个相关计算结果。
许多指令可以很早完成,但是其结果无法正式化。我们无法提交这些结果,否则顺序将出现错误。指令往往是需要按照顺序执行的。像堆栈一样,CPU 将从顶部一直弹出已完成的指令,直到命中未完成的指令。
乱序执行功能让 M1 上的 Firestorm 核心发挥了重要作用,实际上它比英特尔或 AMD 的产品更加强大。
为什么英特尔和 AMD 的乱序执行不如 M1?
「重排序缓冲区」(Re-Order Buffer,ROB)不包含常规的机器码指令,即 CPU 从内存中获取的待执行指令。这些是 CPU 指令集架构(ISA)中的指令,也就是我们称为 x86、ARM、PowerPC 等的指令。
但是,CPU 内部会使用程序员无法看到的完全不同的指令集,即微操作(micro-op 或 μop),ROB 内全是微操作。
微操作非常宽(包含很多位),能够包含各种元信息。而 ARM 或 x86 指令则无法添加此类信息,因为会发生:
程序的二进制文件完全膨胀。
暴露 CPU 的工作原理细节,比如是否具备乱序执行单元、寄存器重命名等详细信息。
很多元信息仅在当前执行情况下才有意义。
你可以将其视为,在编写程序时有一个公共 API,需要保持稳定并供所有人使用,那就是 ARM、x86、PowerPC、MIPS 等指令集。而微操作基本上是用于实现公共 API 的私人 API。
通常,微操作对于 CPU 而言更易于使用,因为每条微指令都能完成一项简单的有限任务。常规的 ISA 指令可能更复杂,会导致大量事情发生,进而实际上转化为多个微操作。
CISC CPU 通常只使用微操作,否则大型复杂的 CISC 指令会让 pipeline 和乱序执行几乎无法实现。
RISC CPU 有一个选择,所以较小的 ARM CPU 不使用微操作,但这也意味着它们无法执行乱序执行等。
对于理解英特尔和 AMD 的乱序执行不如 M1,这很关键。
快速运行的能力取决于你可以用微操作填充 ROB 的速度及数量。填充的速度越快,这种能力就越大,你就有更多机会选择可并行执行的指令,性能就会进一步提升。
机器码指令被指令解码器分割成多个微操作。如果有更多的解码器,我们就可以并行分割更多的指令,从而更快地填充 ROB。
这就是存在巨大差异的地方。最糟糕的英特尔和 AMD 微处理器核心具有 4 个解码器,这意味着它可以并行解码 4 条指令,并输出微操作。
但是苹果有 8 个解码器。不仅如此,ROB 还大了约 2 倍,基本上可以容纳 3 倍的指令。没有其他主流芯片制造商的 CPU 拥有如此多的解码器。
为什么英特尔和 AMD 不能添加更多的指令解码器?
这就牵扯到 RISC 了。M1 Firestorm 核心使用的是 ARM RISC 架构。
对于 x86,一条指令的长度可能是 1–15 字节不等。而在 RISC 芯片上,指令大小是固定的。如果每个指令具有相同的长度,将字节流分割成指令并馈入 8 个不同的并行解码器将易如反掌。但是在 x86 CPU 上,解码器不知道下一条指令从哪里开始,它必须实际分析每条指令,判断它的长度。
英特尔和 AMD 采用暴力方式处理这一问题,它们尝试在每个可能的起点上解码指令。这意味着必须处理大量错误的猜测和错误。这让解码器阶段变得非常复杂,也很难再添加更多的解码器。相比而言,苹果轻轻松松就可以添加更多解码器。
实际上,添加更多东西会导致许多其他问题,以至于 AMD 本身的 4 个解码器基本上已经是其上限了。
而正是这一点让 M1 Firestorm 核心在相同的时钟频率下处理的指令数量是 AMD 和英特尔 CPU 的两倍。
有人可能会反驳说,CISC 指令会变成更多的微操作,它们的密度更大,因此解码一条 x86 指令类似于解码两条 ARM 指令。
然而实际上,高度优化的 x86 代码很少使用复杂的 CISC 指令。在某些方面,它具有 RISC 风格。
但这对 Intel 或 AMD 没有帮助,因为即使 15 个字节长的指令很少见,也必须制造解码器来处理它们。而这会导致复杂性,从而阻止 AMD 和 Intel 添加更多解码器。
AMD 的 Zen3 核心不还是更快吗?
据了解,最新 AMD CPU 核心(即 Zen3)要比 Firestorm 核心快一点。但这只是因为 Zen3 核心的时钟频率为 5 GHz,Firestorm 核心的时钟频率为 3.2 GHz。尽管时钟频率高了近 60%,但 Zen3 也只是勉强超越 Firestorm。
那苹果为什么不增加时钟频率呢?因为更高的时钟频率会使芯片变热。这也是苹果的主要卖点之一。与 Intel 和 AMD 的产品不同,他们的计算机几乎不需要冷却。
从本质上讲,我们可以说 Firestorm 核心确实优于 Zen3 核心。Zen3 只能通过更大的电流和变得更热来维持领先。而苹果选择不这样做。
如果苹果想要更高的性能,他们只会增加更多的核心。这样就可以在降低功耗的同时,还能提供更高的性能。
未来将会如何
看来 AMD 和英特尔在两个方面都陷入了困境:
它们没有允许其轻松追求异构计算和 SoC 设计的商业模型。
传统的 x86 CISC 指令集让它们难以提高乱序执行性能。
但这不意味着游戏结束。它们当然可以增加时钟频率,使用更多的散热,添加更多核心,增强 CPU 缓存等。但它们目前都处于劣势。英特尔的情况最糟糕,因为其核心已经被 Firestorm 击败,并且它的 GPU 薄弱,无法集成到 SoC 方案中。
引入更多核心的问题在于,对于典型的桌面工作负载,使用过多核心会导致收益递减。当然,很多核心非常适合服务器。但 Amazon 和 Ampere 等公司已经使用 128 核的巨型 CPU 了。
幸运的是,Apple 并未出售其芯片。因此,PC 用户只能接受 AMD 和英特尔提供的产品。PC 用户可能会跳船,但这是一个缓慢的过程。人们通常不会立即离开已经有大量投入的平台。
但是,年轻的专业人士没有在任何平台上投入太多资金,他们将来可能会越来越多地转向苹果,从而扩大苹果在高端市场的份额和在 PC 市场总利润中的份额。