翻译:Transwan

校改:Carl Cui

1*AopV0aOKUBJ0vlNvlJGpxg

MLX、oMLX 和 MTPLX 都是跑在 Apple Silicon 上的本地 LLM 工具,它们都强调速度,并且名字相近,这很容易让人把它们当成三个互相竞争的产品,然后问“谁是最快的”。

实际上,它们其中一个是引擎,另外两个是建立在该引擎之上的服务器:MLX 是底层引擎,oMLX 是面向并发和长上下文的推理服务器,MTPLX 是面向单用户低延迟的推理运行时

  • 如果你要做底层开发、训练、微调,或者想控制生成循环,用 MLX;

  • 如果你要服务多个用户、长上下文、本地 RAG,或者模型刚好卡在内存边界上,用 oMLX;

  • 如果你是单人使用,主要跑编码代理或交互式助手,希望输出更快,并且模型带有原生 MTP 头,用 MTPLX;

本文围绕上面这个判断展开。

三者分别处于什么位置

可以先用一句话理解这三层:

  • MLX:Apple 的原始计算基座,为 Apple Silicon 打造的底层框架,其他两者都导入了它。
  • oMLX:为服务多用户而调优的推理服务器,在 MLX 之上做连续批处理、KV Cache 分层和 SSD 缓存,适合多人请求、长上下文场景。
  • MTPLX:为单用户场景调优的运行时,在 MLX 之上利用模型原生 MTP 头做推测解码,适合追求速度的单用户。

MLX 直接面向 Apple Silicon 的统一内存架构。oMLX 和 MTPLX 都构建在 MLX 之上,它们本身不直接和底层硬件打交道,而是通过 MLX 来利用 Apple Silicon 的 CPU/GPU 共享内存能力。

因此把 MLX 拿去和 oMLX、MTPLX 做横向比较并不合理:MLX 是底座,另外两个是建立在底座上的运行时和服务器。

1*RUhL1 SD71bvsiAGbX86Mg

MLX:Apple Silicon 上的本地推理引擎

MLX 是 Apple 机器学习研究团队 ml-explore 开源的 array framework。它的 Python API 很像 NumPy,高层模块 mlx.nnmlx.optimizers 与 PyTorch 高度契合,因此迁移模型时非常直接。

配套的 mlx-lm 可以直接从 Hugging Face Hub 加载大量模型。最简单的调用方式只需要几行 python 代码:

from mlx_lm import load, generate

model, tokenizer = load("mlx-community/Mistral-7B-Instruct-v0.3-4bit")
text = generate(
    model,
    tokenizer,
    prompt="Write a story about Einstein",
    verbose=True,
)

或者在命令行通过下面的 shell 命令直接运行:

mlx_lm.generate --model mistralai/Mistral-7B-Instruct-v0.3 \
  --prompt "How tall is Mt Everest?"

MLX 真正重要的地方,在于它是针对 Apple Silicon 的统一内存进行设计的。

在传统独立 GPU 架构里,CPU 和 GPU 通常有各自的内存池,中间通过 PCIe 传输数据。推理过程中,数据搬运本身就可能成为开销。在 Apple Silicon 上,CPU 和 GPU 共享同一个物理内存池,MLX 的数组可以在 CPU 和 GPU 之间共享,不需要来回复制。

这就是 MLX 的价值:它不是一个“聊天工具”,而是 Apple Silicon 上本地机器学习工作负载的基础层。

但基础层不等于成品工具。mlx-lm 能生成文本,也能处理批量请求,但如果你想长期运行一个本地聊天服务、编码代理、RAG 后端,或者想管理多个模型、多个请求、长上下文和缓存,MLX 本身还不够。这正是 oMLX 和 MTPLX 出现的原因。

本地 LLM 通常会撞上两堵墙

在自己的 Mac 上跑本地 LLM,通常会先撞上两类瓶颈。

第一类是内存与吞吐量瓶颈:

模型权重、KV Cache、长上下文、多用户请求,都会占用统一内存。你可能并不是觉得模型“算得慢”,而是发现 RAM 快被塞满了:想上更大的模型不行,想撑 100K token 上下文吃力,想同时服务多个请求就开始不稳。

第二类是单流延迟瓶颈:

只有一个用户、一个请求,RAM 也还够,但回答就是一个 token 一个 token 慢慢吐出来。原因是自回归解码本质上是顺序过程:每生成一个新 token,都要做一次前向传播。模型越大,每个 token 需要搬运和计算的权重越多。

oMLX 主要解决第一堵墙。
MTPLX 主要解决第二堵墙。

理解这件事之后,做出选择就容易多了。

oMLX:让 Mac 更适合服务多人和长上下文

oMLX 是一个 macOS 原生推理服务器,导入了 MLX,并在其上做了两件关键事情:连续批处理和双层 KV Cache。

连续批处理:提高多请求吞吐

通常推理服务器会处理完一个请求再处理下一个请求,这样做的问题是,每个用户生成 token 时都要单独读取一次模型权重。

连续批处理的思路是,把多个用户正在生成的 token 交错进同一次前向传播。读取一次模型权重,就可以为多个请求各生成一个 token。这样在并发场景下,吞吐明显会更好。

这也是 oMLX 的定位:它不是为了让单个用户的单次回答极限加速,而是为了让一台 Mac 在多个请求、多个用户、长上下文场景下更稳。

双层 KV Cache:RAM 不够时不要直接崩

KV Cache 是注意力机制在推理时保存的运行状态。上下文越长、并发越多,KV Cache 越容易膨胀。传统做法通常是把它放在 RAM 里,满了就驱逐或重算。

oMLX 的做法是把 KV Cache 分成两层:热数据放在 RAM 中,较旧的块可以以 safetensors 文件形式溢出到 SSD。下次遇到匹配前缀时,再从磁盘恢复,而不是从头计算。

这对本地 RAG、多轮对话和重复系统提示非常有价值。因为这些场景里,很多前缀内容会反复出现:系统提示、固定文档上下文、共享的检索片段等。如果能复用这部分前缀,首 token 延迟会明显改善。

oMLX 适合什么场景

oMLX 可以通过 Homebrew 安装并启动一个 OpenAI 兼容服务器:

brew tap jundot/omlx https://github.com/jundot/omlx
brew install omlx

omlx serve --model-dir ~/models \
  --hot-cache-max-size 20% \
  --paged-ssd-cache-dir ~/.omlx/cache \
  --max-concurrent-requests 16

它会在 http://localhost:8000/v1 提供 OpenAI 兼容接口,也支持聊天 UI、Anthropic 风格的 /v1/messages,以及 /v1/embeddings/v1/rerank 等 API 端点。

这意味着一个 oMLX 实例可以支撑一套完整的本地 RAG 后端:聊天、嵌入、重排都在本机完成,数据不需要离开 Mac。

它还提供了更偏运维的能力:当内存紧张时,模型会使用 LRU 机制进行驱逐,你可以固定那些希望常驻内存的模型,为每个模型设置空闲 TTL 以自动卸载,此外还有总内存限制(默认值:系统 RAM 减去 8 GB)以防止失控的加载导致整个 Mac 发生 OOM。一个原生 Swift 菜单栏应用可以启动、停止和监控服务器,并带有持久化统计信息、崩溃自动重启和 Sparkle 自动更新功能。它的气质很明确:不是一次性跑单个 prompt,而是把 Mac 当成本地 AI 服务来长期运行。

MTPLX:让单用户交互更快

MTPLX 解决的是另一类问题:单用户、单请求、RAM 够用,但输出太慢。

它的核心是原生 MTP 推测解码。

一些现代模型,例如 Qwen3-Next 家族,内置了多 token 预测头,也就是 MTP head。这些额外输出头可以低成本地预测接下来几个 token。很多 LLM 运行时会忽略 MTP head,而 MTPLX 会直接利用这些 MTP head 作为“草稿器”。

传统推测解码通常需要一个额外的小模型来先猜 token,再由大模型验证。MTPLX 的特别之处在于:它不需要第二个草稿模型,而是使用目标模型自带的 MTP head。

流程大致是:

  1. MTP head 先草拟 K 个 token。
  2. 目标模型用一次批量前向传播验证这些 token。
  3. 根据概率比接受规则决定保留哪些 token。
  4. 如果某个 token 被拒绝,就从残差分布中重新采样,保证输出分布仍然正确。
  5. 如果 K 个 token 都被接受,还能额外拿到一个奖励 token。

MTPLX 强调的一点是:它不是简单的 greedy argmax 技巧。它使用 Leviathan–Chen 拒绝采样规则来保持输出分布正确。项目说明里给出的数据是:在 Qwen 3.6 27B、temperature 0.6 的设置下,decode TPS 提升约 2.24 倍。

安装和启动也很直接:

brew install youssofal/mtplx/mtplx
mtplx start --profile sustained --port 8000

它提供 OpenAI 和 Anthropic 兼容接口,可以接 Claude Code、Cline、Continue、Open WebUI 等工具。对一个本地编码代理用户来说,这种提升最容易感知:不是服务更多人,而是让你面前这一条回答更快出来。

不过,这个能力有前提。

MTPLX 只对带有原生 MTP head 的模型有效果。目前它的典型收益主要绑定在 Qwen3-Next / Qwen3.6-27B 这类模型上。如果模型没有 MTP head,就不要期待它能带来加速。投入前应该先用:

mtplx inspect <model>

确认模型是否适配。

oMLX 和 MTPLX 不是谁替代谁

oMLX 和 MTPLX 看起来都在做“让本地 LLM 更快”,但它们优化的是不同场景。

  • 单用户、batch size 为 1 时,解码通常受内存带宽限制。GPU 算术单元可能有不少空闲,MTPLX 的推测解码正是把这部分空闲算力用起来:一次验证多个猜测 token,从而让每次读权重产生更多有效 token。

  • 多用户并发场景下,情况会有所改变。多个请求被批到一起,算术单元不再空闲。此时推测解码里被拒绝的 token 也会消耗算力,而这部分算力本可以用来处理其他真实请求。并发越高,oMLX 的连续批处理和缓存管理越重要。

所以更准确的说法是:

  • oMLX 适合“道路拥堵”的时候:并发、长上下文、多模型、本地服务。
  • MTPLX 适合“凌晨空路”的时候:单用户、单请求、追求最快 decode。

理想情况下,未来也许可以把 MTPLX 的单流推测机制集成进 oMLX 这样的高吞吐服务器。但这不是简单拼接两个工具就能解决的问题,因为推测解码会消耗并发服务本来要分配给真实请求的算力。怎么组合,需要非常谨慎的调度设计。

做基准测试时,不要骗自己

本地 LLM 工具的 benchmark 很容易误导人。很多人启动服务器,跑一个短 prompt,看屏幕上的 tokens/sec,然后截图发布。这个数字可能主要测到的是冷启动、内核编译和空缓存,而不是你一天使用中的真实速度

更诚实的测试至少要注意几点。

第一,将 prefill 和 decode 分开看。
首 token 时间主要受 prefill 影响,长文档问答尤其如此;生成 token/sec 则主要衡量 decode。把两者揉成一个数字,会掩盖你真正卡在哪个环节。

第二,在真实 batch size 下测试。
MTPLX 的 2.24 倍加速来自单流场景;oMLX 的优势则会在并发和批处理下显现。用 batch size 1 测 MTPLX,再用 batch size 16 测 oMLX,然后硬比谁更快,本质上是在比较两个不同问题。

第三,预热后再测稳态。
丢掉前几次运行的数据,因为那里面包含内核编译和空 KV Cache 的开销。报告 p50,也要报告 p99。漂亮的 p50 配上难看的 p99,用户体验依然会很糟

第四,固定采样参数。
推测解码的接受率会随 temperature、top_p、top_k 变化。只在 temperature 0 下跑,往往会让结果显得过于好看。真实助手场景通常不会一直用贪心解码。

第五,不要跨硬件比对。
绝对 tokens/sec 受内存带宽影响很大。别人 M5 Max 的数字不是你 M2 的目标,只是另一个硬件环境下的结果。

第六,测端到端请求,而不是只测内核。
快了两倍但给模型喂入了错误检索文档,端到端来看没有变快,反而变慢了。快速得出的错误答案是代价最大的,真实测试应该包含检索、重排、prefill、decode 和答案质量。

一句话:客观有效的 benchmark 往往是枯燥的,但只有考虑到上述问题它才是有效的。

一个实际例子:用 oMLX 做本地 RAG 后端

在这三个工具里,oMLX 最适合直接作为本地 RAG 后端,因为它在一个服务里提供了聊天、embedding 和 rerank 等功能。

典型流程是:

  1. 用 embedding 端点把文档块向量化。
  2. 把向量存到本地索引里,比如 sqlite-vec、FAISS 或 Chroma。
  3. 查询时嵌入用户问题,先召回前 20 个候选文档。
  4. 用 rerank 模型重排候选结果,选出真正相关的前 5 个块。
  5. 把重排后的上下文放进系统提示,让聊天模型生成答案。

embedding 请求示例:

curl http://localhost:8000/v1/embeddings \
  -H "Content-Type: application/json" \
  -d '{"model":"bge-m3","input":["text of chunk 1","text of chunk 2"]}'

rerank 请求示例:

curl http://localhost:8000/v1/rerank \
  -H "Content-Type: application/json" \
  -d '{
    "model":"modernbert-reranker",
    "query":"how do I rotate the API key?",
    "documents":["candidate 1","candidate 2"],
    "top_n":5
  }'

聊天补全示例:

curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model":"qwen3.5-32b-4bit",
    "messages":[
      {
        "role":"system",
        "content":"Answer only from the provided context.\n\nCONTEXT:\n<top-5 reranked chunks>"
      },
      {
        "role":"user",
        "content":"how do I rotate the API key?"
      }
    ]
  }'

这里 oMLX 的前缀共享和 SSD 缓存会派上用场。RAG 应用里,系统提示、固定指令和部分共享上下文经常重复出现。只要前缀能匹配,这些内容就可以复用,减少长 prompt 的 prefill 成本。

如果你是单用户,也可以把 embedding 和 rerank 留在 oMLX,把最终聊天步骤交给 MTPLX,利用它的单流 decode 加速。但如果你要服务多用户,单个 oMLX 实例通常更简单,也更容易控制内存和模型生命周期。

这些坑需要提前知道

MTPLX 只对带原生 MTP head 的模型有效。
2.24 倍加速是真实的,但不是所有模型都有效。Hub 上大多数模型没有 MTP head,开始前先用 mtplx inspect <model> 检查。

MTPLX 的 headline 数据依赖运行配置。
Burst 配置追求最大单流速度,风扇可能更吵,上下文限制更紧。长上下文任务通常应该用 Sustained 配置,不要期待在 100K token 文档上也有同样加速。

oMLX 的 SSD 缓存只有在前缀复用时才有收益。
第一次见到的 prompt 仍然要正常计算。它真正适合的是重复系统提示、多轮对话和共享 RAG 上下文。

第三方性能数字只能当参考。
文章中提到 oMLX 接近 47 tok/s、LM Studio 约 16 tok/s 的对比来自第三方评测,不是 oMLX 官方文档。真正该信的是你在本地模型、量化方式、上下文长度上的 benchmark。

版本偏差会带来问题。
oMLX 和 MTPLX 都依赖 MLX,MTPLX 还可能使用定制 MLX 分支。遇到异常行为时,先检查 mlx 版本和运行环境。

这些工具都是 Apple Silicon 优先。
如果你的目标是 Linux + CUDA 云端部署,它们不是答案。MTPLX 也很直接地说:Linux 场景请使用 vLLM。

RAM 底线是真实存在的。
16 GB MacBook Air 并不是这类工具的理想目标机器。长上下文、大模型、多模型并存,都会很快吃掉统一内存。

最后到底该选谁

如果你要做模型开发、训练、微调,或者希望直接控制生成循环,选 MLX。它是基础层,适合需要自由度和底层控制的场景。

如果你要搭本地服务,尤其是多用户、长上下文、本地 RAG、embedding、rerank、OCR、模型管理、内存限制这些需求都存在,选 oMLX。它的核心价值是让一台 Mac 更像一台能长期运行的本地 AI 服务器。

如果你是单人使用,跑编码代理或交互式助手,模型又带原生 MTP head,并且你最在意“答案快点出来”,选 MTPLX。它的价值不是吞吐量,而是单流 decode 速度。

所以这三个名字看起来像是互相拼错了,但它们其实是一套本地 LLM 推理栈里的三个层次:

  • MLX 是引擎
  • oMLX 是把这台引擎安排成多人服务的服务器
  • MTPLX 是把这台引擎调到单人冲刺速度的运行时

真正的问题从来不是哪个工具更强,而是取决于你本地的工作负载。

原文链接

Apple Silicon’s LLM Stack Has Three Layers. Here’s Which One You Should Actually Be Using.