苹果神经引擎完整逆向:一颗专用AI加速器的兴衰

苹果在 M5 芯片上做了一个安静但重大的结构调整:神经网络加速单元不再是一个独立模块,而是把 ANE 核心折叠进了 GPU 核心内部。苹果的发布会材料把这归结为「LLM 性能」的提升,但对追踪苹果芯片多年的人来说,这个布局变化宣告了一个时代的收尾:从 A11 Bionic(2017)开始存在于每一颗 iPhone 和 Apple Silicon 里的独立 NPU,正在退出历史舞台。

就在这个节点上,一位三年前放弃 ANE 的逆向工程师回到了这个题目上。Eileen Yoon 曾是 Asahi Linux 团队里逆向 ANE 驱动的成员,2023 年她停下工作的理由写在博客里:ANE 的架构过于专用,围绕它构建通用加速平台的努力没有意义,连 macOS 自己都只用 ANE 给访达生成图片缩略图。2026 年 9 月,她发表了题为《Retrospectively Reverse-Engineering Apple's Neural Engine》的长文,把 M1 上这颗 16 核 NPU 的计算单元、任务调度、内存层级的完整内部架构公之于众。文章登上 Hacker News 热榜,三天获得 173 分。

M1 芯片 die shot,神经网络引擎位于芯片中部

这篇文章值得细读,因为它回答了一个比「怎么破解 ANE」更有长期价值的问题:一颗 2017 年定型的 AI 加速器,内部的每个设计决策如何服务于当时的 CNN 工作负载,又如何在 transformer 时代逐一变成枷锁。

逆向的对象:一颗没有说明书、没有指令集的芯片

苹果从未公开 ANE 的微架构文档。开发者能触碰 ANE 的唯一合法途径是 CoreML 框架,模型编译成不透明的二进制后提交给系统,硬件层面发生了什么完全是黑盒。

逆向的突破口在于 ANE 的固件和寄存器。Yoon 此前的工作(已开源为 eiln/ane 仓库,GitHub 上有 518 颗 star)从 Linux 内核驱动入手,摸清了任务提交的寄存器接口;这次的新文章则更进一步,把编译后的硬件配置格式、固件调试例程逐一拆解,交叉证实了内存系统的真实结构。

eiln/ane 仓库,逆向出的 ANE Linux 驱动

逆向得到的第一手结论是:ANE 没有指令集。GPU 执行指令流,CPU 执行指令流,ANE 执行的东西叫「任务描述符」(Task Descriptor)——一个 628 字节的定长数据块,内容是整块硬件的配置寄存器转储。里面依次写着卷积的输入输出维度、权重加载地址、激活函数选择、结果回写位置。硬件拿到描述符后,按固定的数据流走一遍,任务就完成了。

这是一颗「固定功能数据流引擎」:不能执行任意算法,只能按配置好的参数走完一遍固定数据流。Yoon 用一个 1×1 卷积的最小任务做了解剖:描述符头部声明依赖关系,KernelDMA 段配置 16 条权重加载通道,Common 段写明张量几何(输入 8 通道、输出 3 通道、4×1 输出尺寸),TileDMA 段配置输入输出的内存布局。每个段对应 ANE 数据通路上的一组寄存器块,起始地址从 0x26bc00000 排列到 0x26bc25000,各占 0x1000 字节。

计算核心:2048 条乘加通道的通用与不通用

ANE 的计算能力由 16 个计算核心提供,每个核心含 128 条 FP16 乘加(MAC)通道(INT8 模式下 256 条),全芯片共 2048 条并行通道。单看这个数字,它和同期任何 AI 加速器没有本质区别——矩阵乘法、卷积、注意力,底层都是点积,点积就是乘加。

把 ANE 与通用加速器区分开的也不在乘加通道本身。真正起决定作用的是乘加周围的资源布置方式。Yoon 强调了这一点:MAC 通道不感知自己在算卷积还是矩阵乘,感知工作负载类型的是数据进出乘加阵列的通路。

三个具体的硬件细节可以说明这颗芯片的定制深度:

定点累加器。 每条 MAC 通道的乘积累加进一个 32 位寄存器,用的是 Q16.16 定点格式(16 位整数部分、16 位小数部分),只在读出时转成 FP16。Yoon 的验证方法是构造一个全 1 向量点积,让累加器持续增长,观察输出何时饱和。实验结果显示,数值涨到 32768(定点数 0x7800,恰好是一个合法的 FP16 值)之后输出直接跳变成 +∞(0x7c00),说明饱和发生在累加器内部的 2¹⁵ 边界,而不是 FP16 输出格式溢出。这个探针实验确定了 ANE 内部算术的精确形态:整型运算、定点累加、读出时格式转换。

查表式激活函数。 tanh 这类非线性激活在 ANE 上不靠计算,靠查找表。往 CoreML 里塞一个 tanh 层,编译后检查硬件配置区,能找到 33 个连续的 FP16 系数,逐个对照后确认是 tanh(i/8) 在 i=0 到 32 处的量化采样,覆盖 [0, 4] 区间、步长 1/8。换算成插值行为:硬件把输入映射到查表坐标,取相邻两个表项做线性插值。Yoon 用单脉冲查找表做了验证,扫描输入得到的输出呈三角形,插值假设成立。33 个点定义 32 个区间,硬件用 2 的幂缩放输入,R=3 时表项间距 1/8。

激活融合。 乘加结果直接流入激活单元,不回写内存。这依赖激活函数逐点运算的性质:一个标量的约减完成后,它的激活只取决于这个标量自己,可以立刻计算。偏差项(bias)和线性缩放也在编译期折叠进同一条路径。每省一次内存往返,对带宽受限的工作负载都是净收益。

内存层级:162:1 这个数字定下的规矩

ANE 性能分析里最重要的一个数字是算术强度门槛。M1 的 ANE 标称 11 TOP/s 算力、系统内存带宽 68 GB/s,两者相除得到 162 OP/byte:从 DRAM 每取 1 字节,平均要支撑至少 162 次运算,内存带宽才不是瓶颈。

反过来看更直观。FP16 乘加每做 2 次运算要读 2 个 2 字节操作数,即 0.5 OP/byte。如果所有操作数都从 DRAM 直取,喂饱 11 TOP/s 需要 22 TB/s 的内存带宽,是实际带宽的 300 多倍。结论只有一个:操作数必须在片上复用,DRAM 流量主要是权重和输入的一次性装载。

围绕这个约束,ANE 布置了三级片上存储:

  • KMem(权重存储):每个计算核心私有 64 KiB SRAM,16 核合计 1 MiB,存放当前任务的权重;
  • L1:每个核心的乘加输入暂存区;
  • L2:全芯片共享一块 2 MiB SRAM,中间结果可以在这里留给下一步用,不必回 DRAM。

文章里最扎实的证据来自 ANE 固件本身。Yoon 反编译了固件里的任务调试例程,发现它依次把 16 个 KMem(每个 0x10000 字节,即 64 KiB)和一整块 0x200000 字节(2 MiB)的 L2 转存成文件——固件自己就是按这个结构理解硬件的,逆向推断由此变成实锤。

数据搬运由三组 DMA 引擎分工:KernelDMA 专管权重从 DRAM 装进 KMem,TileDMASrc 把输入块装进 L2,TileDMADst 把结果从 L2 搬回 DRAM。这套分工暴露了苹果在 2017 年定下的三条假设:权重是独立于输入的一类操作数,值得给专用通道;权重只装载不回写,通道做成单向即可;权重会长期驻留反复复用,所以给每个核心复制一份私有存储。在卷积网络里,这三条全部成立——一个卷积核滑过整张特征图,装载一次用几千次。

Transformer 为什么在这颗芯片上跑不顺

transformer 改变的是操作数的复用模式。卷积网络里权重是常量、激活是数据;自回归解码里,生成每个 token 都要把整个模型的权重完整过一遍,「装载一次复用千次」的稳态不复存在,每次推理都是全量重流。这在数学上仍是矩阵乘法,但操作数的生命周期完全变了。

Yoon 用带宽实测量化了后果。她构造了读带宽受限的工作负载(缓冲区远大于缓存、数据只消费一次),拟合执行时间对数据量的斜率,得到三组持续读带宽:KernelDMA 37.99 GB/s,TileDMA 59.08 GB/s,作为对照的 GPU(Metal shader)77.70 GB/s。两条 ANE 通道加起来理论上能到 98 GB/s,但联合实验显示两条通路的耗时严格相加(拟合式 T_AB = 0.001 + 0.939·T_A + 0.981·T_B),说明权重流和输入流的 DMA 请求是串行发出的。单 token 解码吞吐因此被双重压制:每条通路都跑不赢 GPU,两条通路还不能并行。

更结构性的问题是 KMem 的装载路径。ANE 的权重只能从 DRAM 装进 KMem,没有「从 L2 装进 KMem」的通路。对卷积任务这无所谓;但 transformer 推理里恰恰会出现「驻留在 L2 里的张量需要被当作权重用」的场景——KV cache 增长时的重复计算、多步共享的中间激活,都撞在这堵墙上。Yoon 的判断是,苹果在 2017 年根本没预期过「L2 里的数据会变成权重」,这在当时是完全合理的假设。她顺手做了一个思想实验:如果给 KMem 加一条 L2 通路,解码性能不会有现在这么难看,而芯片布局上这并非做不到——M1 的 die shot 上,16 个核心围着中央的 L2 矩形排成 7+7+2,侧面核心的 L2 接口横向进入,顶部两个核心的接口旋转 90 度纵向引出,同一个宽接口换个方向布线而已。

独立 NPU 的兴衰逻辑

把全文的发现收拢,可以得到一条完整的因果链。

2017 年,A11 Bionic 里的 ANE 面对的是这样一幅图景:手机上的 AI 负载以图像类 CNN 为主,模型结构编译期已知,权重静态,复用模式可预测。专用数据流引擎在这个设定下是最优解——固定通路省面积、省功耗、延迟确定,162:1 的算术强度门槛靠片上复用就能跨过。同样的思路之下,那几年几乎所有手机 SoC 都长出了自己的 NPU。

transformer 带来的变化不在算子层面(还是矩阵乘),而在内存行为层面:权重复用率塌缩,内存流带宽成为唯一重要的指标,而这恰恰是通用 GPU 的强项——GPU 的 DMA 可以维持大量并发的内存请求,把它喂满。ANE 的串行 DMA、只读单向的权重通路、L2 到 KMem 的缺失通路,每一条 2017 年的正确决策都在新负载下变成了限制。等大模型成为主要负载时,所谓「神经引擎」对它们的加速空间已经很小,Mac 上跑 LLM 走 Metal 与 GPU 是社区多年验证过的现实路径。

M5 把 ANE 核并进 GPU 核,可以读作对现实的追认:既然 transformer 时代的高效计算单元是 GPU 的数据流,那就在 GPU 里保留一小块专用算力处理 still-profitable 的负载(摄像头管线、音频、端侧小模型),而不是维持一个独立的大 NPU。Yoon 的原话更干脆:独立 NPU 的「终局开端」。

逆向工作本身还留下一个贴合主题的注脚。三年前她放弃 ANE 的理由——架构太专用、负载面太窄——和今天她回头补完架构图的理由,是同一个判断的两面:正因为这颗芯片把 2017 年的负载假设浇筑进了硅片,它的内部结构才成为一份关于「AI 加速器专用化代价」的完整标本。A11 到 M4 十年间的每一次 iPhone 发布会都在报 NPU 的 TOPS 数字,这篇文章把数字背后的数据流画了出来,也画下了它的边界。

参考