---
layout: post.njk
source: https://yage.ai/share/iphone-mac-local-llm-decisions-20261010.html
speaker: yage.ai
title: |-
  用 iPhone 给 Mac
  加速大模型，看起来很疯，但又没那么疯
date: '2026-10-10'
summary: 文章解析开源项目Backburner如何利用iPhone作为外部内存池加速Mac本地大模型推理。核心策略包括利用注意力机制的数学可切分性进行状态划分，将静态历史键值编译为Apple神经引擎权重以规避带宽瓶颈，并通过架构减法优化窄链路通信，展示了在物理资源受限下的分布式推理创新。
area: tech-engineering
category: ai-ml
tags:
  - distributed-inference
  - attention-mechanism
  - edge-computing
  - memory-optimization
  - hardware-acceleration
people: []
companies_orgs: []
products_models:
  - backburner
  - qwen3.8-27b
media_books: []
draft: true
status: evergreen
---

一个iPhone用10Gb/s的USB-C线挂在macbook旁边给本地大模型加速，第一眼看上去是种华而不实的geek
toy。手机那点计算能力+一根物理线带来的通信延迟很容易杀鸡用牛刀。

我一开始看这个开源项目 backburner，也是这么想的。看了代码之后才发现很多人误解了。作者并不是想在手机上运行大模型。他始终在
Mac 上进行计算，并没有在手机上进行整个推理。

在这个系统中，MacBook Pro
是一个对外提供服务的宿主，上面跑着一个普通的本地服务端。比如一台 24 GB
内存的 Mac，读到长文本的时候内存吃紧，那部用线缆连着的 iPhone
就充当挂在线另一头的外部内存池和协处理器。它负责两个环节：一是在 prefill
阶段（读入长文本时）分担一部分网络层，二是在 Mac
装不下超长上下文时接管溢出的历史键值缓存。这两个概念你可能不陌生：prefill
是把整段输入一次读完，decode
是之后一个一个往外吐词。手机在这两个阶段里的角色完全不同，后面会看到。

即便你手边没有 iPhone
或内存小一点的电脑，这依然是一个有意思的场景。它描述的是许多人都可能遇到的困境：物理资源受限，但计算任务还要继续。长文本填满了主机的显存，外接设备的算力并不对称，二者仅以一根线相连。在这种限制下，下一步该怎么走？跳出具体机型，这种在物理约束中做取舍的思路，对多卡并行、分布式推理乃至边缘设备协作都同样适用：手头的资源有限，可任务必须完成，突破口在哪里？

## 先挑能在数学上无损切开的工作

人的本能会先去优化这根线——测通信带宽够不够，压协议延迟，把要发的数据高比压缩。但这样容易让人误以为只要数据跑得够快，两边的计算就能自然对接上。

干过分布式的人都知道，这里面有个最大坑。两台机器能不能一起做一件事，决定因素不是网线跑多快，而是你要交给它们做的计算有没有办法在数学上独立求解、最后还能无损合并。网线带宽只是第二步的事。

如果一个算子切分后仍要求大量的跨设备数据依赖，两头就需要同步中间状态。这种情况下即便线缆延时很低，往返的等待时间也可能抵消多设备分担算力带来的好处。这种算子不适合这里的切分方式。

在这个系统里，被选出来分流的计算是注意力。这是因为注意力有一个数学性质，就是它天然就可以做局部独立的计算。

当模型的全注意力层遇到长上下文时，它需要把当前的查询向量和历史上所有出现过的键做点积，再用注意力权重对值求加权和。文本越长，历史键值缓存就越大，最终会超出单机的内存容量。这时系统就让
Mac 把较早的那批键值缓存整体搬到手机上去托管，但前馈层和模型权重还是在
Mac 本地。

在进行注意力计算的过程中，Mac负责将当前的查询向量通过数据线发送给手机。手机则仅对其所存储的历史键值页进行局部注意力计算，得到一个局部结果，包括局部输出向量、局部最大值以及指数和。随后，手机将归一化后的局部输出向量和由最大值、指数和算出的对数归一化量返回给Mac。Mac利用对数求和指数公式，将手机的局部结果与本地的结果进行合并。

左侧是不可切分的算子，全局依赖导致两端频繁同步、收益被线缆延迟吃掉；右侧是可切的算子，按位置切开后各自独立求值、再精确合并

所以这样的分割，得到的结果和之前是一样的。从数学上讲，局部注意力合并起来是完全等价的，这种分割没有额外引入有损量化或者近似截断。在默认模式的贪婪解码测试下可以看到，生成的每一个词元都和单机逐字的结果完全一致。要想让两台设备合作，先把数学上能拆开的计算挑出来，比到处找更快的传输线实用多了。

## 状态组织的接缝决定了切分点

一旦确认了注意力算子可切分，下一步自然的直觉就是按两台设备的算力大小来分配计算量——主机强就多给几层，副机弱就少给几层，只要两边时间对上，吞吐就稳。

但一旦真的去切模型，往往会发现这样切非常不work。因为很多模型里面不同的层记东西的方法是不一样的。

比如这个项目用的
Qwen3.8-27B。它的大部分层不存每个历史词，而是记一个固定大小的摘要，新词来了滚动更新一下，前面信息被揉进摘要。这样省内存，但摘要就是一坨，没法把前半段历史抽出来给另一台机器，因为不再分了。摘要还往前滚，想回头只能从头算。

而另外的少数层用的另一种方法，给每个历史词单独存一行，攒成一张随上下文变长的表。这个表的每一行彼此独立，最早的几行可以整片挪走。这两类层在整个网络里交替出现：连续
4 层里，前 3 层是上面那种摘要层，第 4 层才是这种存表的层，然后 4
层一循环，一直到模型末尾。

这直接决定手机能干啥。第一种层只有一个整块的摘要，要给手机用只能把整层挪过去，没法按位切。第二种层有一张表，可以把表里最老的几行挪到手机内存里留着，位置随便切。于是系统的整层切分点必须对齐每
4
层一次的循环边界，顺着数据自己的接缝来。能切哪儿，受这两类层的排列约束，具体切在哪个边界仍由工程师选择。

切好之后，在默认配置不超过 64k 上下文的 split prefill 中，Mac 跑前 40
层，把中间结果扔到手机继续算后面的 24 层；每批最后一个 ubatch 则全部留在
Mac 上跑。看到这儿容易犯嘀咕：Mac 先算完 40
层再交给手机，这不还是你先我后，能快在哪？诀窍是流水线。长输入本来就切成一批批小块送进来，Mac
算完一块的前 40 层，不等手机，把中间结果发过去就转头去算下一块的头 40
层。手机还在啃第一块的尾巴，Mac
已经进了第二块。两台设备在同一段时间里各忙各的，前后依赖被流水线盖住一部分，等待就减少了。

## 把不再改动的数据编译成硬件算力

做完状态切分，系统立刻被另一个端侧推理的常见瓶颈卡住，它在 decode
阶段。在全注意力层，每个词元出来，芯片都要读取之前的键值缓存，越长越贵，内存带宽就可能成为瓶颈。

对这个瓶颈，一般的思路是优化缓存的布局，或者对内存传输做一些小改动。但仔细看自回归推理的过程，就会发现一个容易被忽视的情况：那些几十步之前生成的历史键值，在整个
decode
中只会被频繁读取而不会被修改。既然这些数据已经固定，还一直把它们当动态数据从内存里读，就非常不划算。

开发团队基于这个洞察换了一种方案。手机里有个 Apple
神经引擎（ANE），它可以运行以固定权重做矩阵乘法的模型。于是系统把副设备持有的历史键值页，一页一页地直接编译成一个
ANE 模型文件，这些页面在 prefill 阶段就陆续在后台编译，详见 神经引擎实现文档。编译好的模型里，原本在内存里的键值就直接固化成了静态权重。

等到 decode 时主机发来新的查询向量，副设备把查询向量作为输入送进这个
ANE
模型，由它计算已编译页面的局部注意力结果。查询向量每步更新，已编译页面的历史键值作为模型权重保持不变，并不是不再需要内存访问。

从冻结历史页、编译为硬件权重、输入查询、硬件矩阵乘到结果一致的一条链路；对比传统路径反复读取内存总线、受限于带宽

这个把历史缓存搬上手机、并且用手机分担旧上下文的注意力机制，在项目的设计说明里有更详细的解释。实测发现，在
A18 的旧页注意力算子测试中，这种把静态数据编译成硬件权重的做法比手机的
GPU 通用图形处理器快 2-3 倍。页权重采用 fp16，没有采用精度不足的 int8
页权重；在 51k 真实会话测试中，33
个词元与精确路径全部一致。数据不变，就把它交给硬件当权重去算，旧页注意力计算就这么得到加速了。

## 面对狭窄链路先做传输减法

即使确定了数据的表示形式和算子的划分方法，两个设备之间仍然需要通过一条链路进行通信。主机设备计算速度快，但副设备相对较慢，这条链路的任何不必要延迟都会导致整个流水线的速度下降。

很多分布式系统遇到这种情况，就会在通信层不断加戏，堆调度协议、堆多路复用、堆重试机制，用更精巧的管道工程去应付窄带互联。这套系统反其道而行之：线窄，那就在架构上做减法，让绝大多数数据根本不用过线。

它在线缆两端做了三条减法。

首先，在手机端镜像自己那些网络层的键值缓存和循环状态，跨线同步的只有新增加的几行。

第二，层间只传残差。这里不传输层内中间张量，而是传输层间残差向量。

第三，在 split prefill 模式下，终点计算在本地。每批输入的最后一个
ubatch 强制在Mac上本地运行。这样，词表维度巨大的 logits
张量留在主机上，无需过线。

主机前序层与手机后序层的时间轴：大部分工作留在各自本地闭环，只有新增增量、层间残差等很少的数据穿过线缆，两端的计算块在时间上重叠把传输藏起来

在大幅降低跨线数据量后，又加入了时间上的流水线：超过 64k
后，512-token ubatch 交错成两半，Mac
负责一半词元的全部网络层计算，手机同时计算另一半词元对旧键值的注意力。两者在时间上交错。这样在
140k 上下文测试中，prefill 阶段吞吐从 58 提升至 68 tok/s，约提升
17%；约三成的提升则来自 32k 和 48k 的 split prefill
测试。窄链路上，花时间搭管道收益有限，更管用的是靠本地闭环削减大部分跨线交互。

## 舍弃负优化与看清真实的收益边界

观察一个系统设计得怎么样，不能只看它实现了什么功能，还要看它在实验中发现并抛弃了什么。在纸上谈兵的时候，很容易构想一些看似合理的协作模式，比如
Mac 自己有 ANE，为什么不利用它在 decode 的时候也加入计算？手机里面有 GPU
也有
ANE，为什么不把两者都利用起来？但真正到了硬件上面，往往会发现这些设想是错误的。项目记录了几种经测量后禁用或搁置的方案：

早期他们尝试过用 Mac 自己的 ANE 来帮忙
decode，但测量后发现生成速度反而慢了 26%，因为 ANE
在频繁读写内存时和图形核心抢总线，拖慢了主计算通路。这个想法也被删。

手机端也试验过让图形核心和 ANE 一起跑
prefill。两者都忙起来会打架，ANE 的吞吐直接掉很多，两者合起来只有单方的
1.1-1.25 倍，所以方案被搁置了。

他们也尝试过在运行时动态学习 ANE 和图形核心的分片比例。但 ANE
空闲时会降频，探测请求的耗时忽高忽低，把在线调度逻辑搅乱了，最后还是换回了一个固定的静态常数。

除了去除反例之外，这个系统非常坦诚地说明了它的局限性。在默认模式下，对于
64k 以下的中短文本，它只依赖 Mac 进行 decode，因为此时副设备没有参与
decode，也就谈不上加速。而对于不超过约 512
个词元的轻量读取请求，则干脆不启用 split
prefill，直接在主机上单机处理，以避免通信成本不划算。同时，该系统对使用场景也有要求，比如在移动操作系统中，由于系统不允许
app 在后台使用
GPU，因此如果手机参与计算的话需要前台亮屏。此外，整个系统目前也只支持单请求串行。

这整套设计真正解决的是从跑不起来到能稳定跑起来，而不是把常规推理速度翻番。一个
8GB 内存的主机装不下项目使用的 27B 模型，24 GB
主机在原有配置下也装不下超出容量的长文本缓存；用一根线把外部设备拉进来协同，这道门槛被推开了。把不可切分的工作挡在外面，顺着数据结构的接缝划分流水线，把凝固的数据转成前向算力，在窄链路上做足减法，这套在极端限制里逼出来的决策路径，即便拿掉线缆和具体设备，遇到资源瓶颈时照样能用。

评测维度

场景与配置基准

实测表现或测量基线

长文本预填吞吐提升

16k / 32k / 48k 上下文

单机 109 / 101 / 87 tok/s 提升至协同 157 / 130 / 113 tok/s

冷启动大文本首字响应

27k 词元会话首次应答

原版 245 秒，单机优化 228 秒，接入协同加速降至 168 秒

专用硬件算子求值耗时

每 16k 键值页（4 个头）

耗时 1.85 毫秒（折合每 1k 键值 0.113 毫秒），达通用核心 2-3 倍

超长上下文单步耗时

140k 极端深度逐字生成

纯通用计算 279 毫秒/词元，按层固化 2 页降至 208 毫秒，3 页降至 176
毫秒

物理线缆交互延迟基准

经手机 app 的 USB-C 16 字节请求/回复往返时间

中位数 96 微秒，90 分位值 183 微秒

极限受限场景从零到一

8 GB MacBook Neo + iPhone Air，以 split decode 运行 27B 模型

使原本装不下的模型能运行，实现协同解码 3.9 tok/s 与预填 64
tok/s

现在大语言模型的架构正在变得越来越混合注意力，那系统状态里面可切分和不可切分的比例也在发生着变化，未来的模型会有更多定长的循环状态，更少的注意力层，但是每个注意力层的计算会更重，这又会给主从计算节点之间带来什么样的切分界限呢？