← 返回文章Decode speed by configuration (tok/s, M1 Ultra, 27B-4bit)
GPU active residency during decode (powermetrics, %)
发现
Copy article
Qwen 3.8-27B 在 M1 Ultra 上:从 12.1 到 20.3 tok/s 通过测量一切
5 个受控实验在 128 GB M1 Ultra 上运行 Qwen 3.8-27B:调度器 QoS 固定,MTP 预测解码,GGUF 挑战者,以及一个 MoE 在调度开销上失败 — 与 Kimi K3 在高思考模式下运行,每个调用都有 powermetrics 证据。
Developed by Robert E. Beckner III (Merlin) | rbeckner.com
Qwen3.8-27B 的权重在早上到达, 同一天我开始与 Kimi K3 在高思考模式下的冒险。 我用一个问题启动了整个过程:这个新的密集 27B — 轻易成为其尺寸类别中最强大的开放模型 — 能否在配备 M1 Ultra、20 CPU 核心、128 GB 统一内存和 800 GB/s 内存带宽的 Mac Studio 上以真实速度服务我的编码代理? 常识认为密集模型在 Apple Silicon 上解码慢,因为它们受内存带宽限制,我想知道这么多 RAM 和带宽是否能打破这一模式。 4 位 MLX 权重达到了 15 GB,框架是最新的,盒子上没有其他运行,第一数字是 12.1 tokens per second. 这感觉不对。
我已经在这类实验上打转了一段时间。 回到三月,在我的自主改进实验室与另一 AI 合作时,我们追踪了 M4 Max 上 30B 模型的 20 tok/s 以下墙,原因是 OS 级分页导致带宽饥饿 — 这项调查留下了一个手册:
powermetrics 首先,每簇采样,内存固定在责备之前。 这次运行从纯粹好奇开始:我听说过 Qwen3.8 的优化,我想知道它是否能在这台机器上以如此多 RAM 立即快速运行。只有在实验中途,带宽受限的现实才让自己显现。 随后是一系列我们共同设计的受控实验冲刺 — Kimi 起草假设并在我身边读取硬件计数器,而我则保持手在金属上。每个实验都旨在为堆栈的单层进行定罪或无罪判决。 胜利来自 2 这些本地推理传说低估的地方:CPU 调度器提示和预测解码。 2 时尚替代方案 — 由 llama.cpp 提供的 GGUF 构建,以及在纸面上看似无敌的专家混合模型 — 都在测量中失败了。 本文是我们共同构建的完整证据链,因为该方法被证明比任何单一数字更有价值。网络中继和服务器是最初的嫌疑人,二者都无辜
服务器路径有 3 跳:我的工作站、一个 SSH 基础的执行中继,以及 Studio 的回环上的模型服务器。 责怪中继本来很容易,所以我们先测量了它。 服务器自身的计时显示 12.1 tok/s 解码;一次原始的进程内生成基准测试,没有服务器也没有中继,产生了 11.5 tok/s. 传输成本大约为 25% 通过每个连接的开销,模型本身是最慢的层。
一个传输错误本来就值得修复,它是那种如果你从未见过就会花费数小时的细节:一个 Python TCP 中继使用缓冲的
read(65536),会在与一个话多的本地协议竞争时死锁,因为缓冲读取器在返回前会等待缓冲区满或 EOF。 切换到 read1(),它在单个底层读取后返回,使中继行为正常。 症状看起来像网络停滞;原因是 stdio 语义。传输被排除后,我们通过常规的 sysctl 层进行排查。 提升 GPU 有线内存限制(
iogpu.wired_limit_mb)在 128 GB 空闲 RAM 下没有任何变化,所以我们恢复了它。 Python 进程是本地 arm64,MLX 报告 GPU 为其默认设备,powermetrics 显示 GPU 在 54-62% 活跃驻留期间在解码时消耗 20-23 W。 这一轮的每个嫌疑人都被排除——这本身就是有用的结果,因为它把调查指向了 CPU。调度器将推理任务放在效率内核上
每个 CPU 集群读取
powermetrics 而不是每台机器读取,暴露了第一个真正的发现。 在解码期间,效率集群以 75-93% 的忙碌度运行,而性能集群在 1-12% 处空闲。 任何在 SSH 上启动的进程都会继承 macOS 读取为低优先级工作的服务质量类,调度器通过将延迟关键工作保持在慢速内核上来遵守该提示。修复方案是 1 行 ctypes,在模型权重加载之前执行:
python
import ctypesctypes.CDLL("libSystem.B.dylib").pthread_set_qos_class_self_np(0x21, 0) # QOS_CLASS_USER_INTERACTIVE
该固定点,加上通过
mlx-lm 而不是完整视觉堆栈加载模型的文本塔,将原始解码速度从 11.5 提升到 15.1 tok/s——从调度器放置获得 31% 的增益,并且加载器更精简,模型保持不变。 我们后来了解到固定点的影响范围有限:它会移动调用线程,而 MLX 的工作线程保持自己的调度类。 对于此模型,主线程承担了足够的工作量以产生影响。预测性解码提供了系统无法实现的跳跃
最大的单一收益来自于基础模型已经拥有的功能。 Qwen3.8 带有一个多标记预测头——一个小型辅助模块,可提前草拟多个标记——MLX 转换器在转换期间悄悄丢弃这些 15 张量。 社区包将头重新托管为独立的 253 MB 草稿器,使我能够将其与训练时一起使用的模型配对。
服役模式是草稿-然后-验证:草稿器提出几个标记,完整模型在 1 通过中检查它们,所有被接受的标记都计数。 在服务器上使用
--draft-model 和 --draft-kind mtp,草稿接受率在现实编码和聊天提示上测得 94%,解码速度从 15.1 提升到 20.3 tok/s 服务器端——通过中继的端到端 13.4 tok/s,提升至 9.0。 GPU 讲述了确认的故事:77% 活跃驻留,全部以完整的 1296 MHz 运行,功耗为 48 W,性能集群 97% 处于繁忙状态。 Prefill 在同一升级中得到改进,从 14.7 tok/s 提升到 18-55,取决于提示形状。Chart data
| MLX stack | llama.cpp Q4_K_M | |
|---|---|---|
| raw stack | 11.5 | 9.2 |
| + text tower | 13.6 | |
| + QoS pin | 15.1 | |
| + MTP drafter | 20.3 | 12.2 |
GGUF 挑战者以 40% 失利
社区帖子将 llama.cpp 与投机性草稿放在 25-32 tok/s,适用于此模型类别的 M1 Ultra,明显领先我们的 MLX 结果,因此我们给挑战者一个公平的机会:官方 Q4_K_M GGUF、匹配的 MTP-only 草稿模型,以及已确认 Metal 加速的当前 llama.cpp 构建。 GGUF 轨道测得 9.2 tok/s 基线和 12.2 采用草稿——约 40% 落后于其旨在推翻的 MLX 堆栈。
llama.cpp 仍是卓越的工程;差距可能在于每个运行时的 Metal 核心如何处理此检查点在此芯片上的表现。 持久的教训更简单:某人在其机器、构建和检查点上测得的数字,是对你的一种假设。 18 GB 的挑战者权重同日下午离开机器,50 MB 的 llama.cpp 二进制文件保留以备未来对决。 我曾在此规则之前写过,作为 benchmark-driven development — 它带来了 SEOReport from heuristics to a product — 在此处节省了迁移成本。
一个 3B 活跃 MoE 本应获胜,GPU 计量器解释了失败
最后一位挑战者拥有最强的理论 — 我们一开始就想测试的相同常识。 密集模型在 Apple Silicon 上解码慢,因为每个生成的 token 都需要读取几乎所有权重;一个 35B 总参数的专家混合模型每个 token 仅激活约 3B,因此在带宽受限的机器上,其解码速度应比 27B 密集模型快数倍 — 每个 token 读取约 11% 的权重。 我们下载了 4-bit MoE 及其匹配的 MTP 草稿器,预热服务器,并测得 14.7 tok/s 原始:与它本应羞辱的密集模型相同速度。 使用投机解码时,在现实提示下达到 19 tok/s,在高度可预测文本下达到 26.3 — 与密集模型的 20.3 持平,且没有质量论点打破平局。
powermetrics 在 1 个样本中识别了该模式。 在 MoE 解码期间,GPU 的活跃驻留率为 0-3%,而效率集群在 60-90% 之间下降。 模型将每个 token 的预算用于 CPU 端工作,操作级计时显示原因:每个调度操作成本 50-100 微秒的开销,这种混合架构 — GatedDeltaNet 线性注意力加 256 个专家,隐藏状态宽度 2048 — 每层大约发出 78 次操作,跨 40 层。 在批量大小 1 下,GPU 在 CPU 能排队下一个之前完成每个小内核。 机器受调度限制,调度受限工作负载无法从每个 token 读取更少权重中获益。Diagram source
graph TB
A[解码慢
在批量 1] --> B{GPU 忙碌
在解码期间?}
B -->|是| C[带宽受限
更少字节更胜]
B -->|否| D[调度受限
更少操作更胜]
C --> E[MoE 帮助
更小量化更助]
D --> F[投机解码
最有帮助]
style E fill:transparent,stroke:#10B981,stroke-width:2px
style F fill:transparent,stroke:#3B82F6,stroke-width:2px为闭环,我们用合成占用测试证明 GPU 路径本身是健康的:一个 8192³ 矩阵乘法循环在 bfloat16 下达到 10.8 TFLOPS,GPU 处于 97-100% 驻留率和 68 W。外部证据与本地诊断一致 — 独立估计将此 MoE 类别放在 M2 Ultra 上约 21.7 tok/s,且 OpenVINO 问题文档记录同一模型在后端更弱的调度路径上输给 8B 密集模型。 稠密的 27B 及其 MTP 起草者保留了生产槽,38.5 GB 的 MoE 权重同一天晚上被删除。
Chart data
| GPU active residency (%) | |
|---|---|
| dense 27B + MTP | 77 |
| MoE 35B-A3B | 2 |
| matmul hog test | 100 |
实验留下的 5 控制实验
机器现在以 3.8-27B 的速度在服务器端每秒处理 20.3 个 token,比一天开始时提升了 68%,更持久的收益是我们将在每个未来的盒子上重用的检查清单:
- 批量大小为 1 的解码有 2 个模式,带宽受限和调度受限,2 秒的
powermetrics样本在你在错误修复上花费下载之前就能识别你的情况。 - 预测解码是单用户盒子中最高杠杆的服务旋钮。 它在此处添加了 34% 并与每个其他优化叠加,因为验证批次在 GPU 上工作,而起草者吸收空闲间隙。
- 调度器 QoS 是 macOS 上的真实推理参数。任何在 SSH 以上启动的都应有意锁定其线程,锁定的效果可按集群可验证,而非凭直觉。
- 社区吞吐量声称在检查点、构建和芯片之间传播不佳。 本地基准测试比迁移更便宜。
- 删除是工作流程的一部分。 每个失败的挑战者在当天内就将磁盘清空,这保持了下一个实验的诚实性和机器的轻量化。
这段冒险中令人满意的部分是答案都在硬件计数器中等待有人提出精准问题——这一次我们一起问了它们。 你测量的本地模型价值超过你猜测的更大模型——我从将 local AI gains as infrastructure 视为基础设施而非琐事中获得的同样复合回报。 我在权重落地的早晨启动了整个过程,Kimi K3 在每个实验、每个计数器读取以及同一晚的实验室笔记写作中都坚持战斗。 下一次实验已排队:编译的解码图承诺压缩决定 MoE 判决的每个操作调度成本,当 MLX 版本发布时,重赛将耗时一个下午和一次下载。
#AI#apple-silicon#benchmarking#local-inference#mlx#performance#experiential#insights
Apple Silicon inference, measured
Qwen 3.8-27B 在 M1 Ultra 上:从 12.1 到 20.3 tok/s 通过测量一切
MTPLX 在 M1 Ultra 上:25 tok/s 调优 那个服务了 16.5