教程·阅读约 3 分钟·
让 macOS 虚拟机里的 llama.cpp 提速 16 倍:Metal 能力 shim 实操指南

让 macOS 虚拟机里的 llama.cpp 提速 16 倍:Metal 能力 shim 实操指南

Apple 的 Virtualization.framework 会给 macOS 虚拟机里的虚拟 GPU 上报保守的 Metal 能力,导致 llama.cpp 只能走慢速 GPU 路径。用 Cua 开源的一个进程级 shim,可以把提示处理速度提升 7-11 倍、token 生成速度提升 8-16 倍。

原文来源:trycua/cua 官方博客 — 用 11 行配置和一个小 dylib,让 macOS 虚拟机里的 llama.cpp 从保守的 Metal 路径切到新内核,推理速度最高提升 16 倍。

在 macOS 上跑 macOS 虚拟机(比如用 Apple 官方 Virtualization.framework 或 Tart、Lume 这类工具),然后在虚拟机里跑 llama.cpp 跑本地模型——这种"套娃"玩法现在越来越常见,因为隔离环境对搞 AI 实验来说太方便了。但很多人发现一个诡异的现象:虚拟机里的推理速度比宿主机慢一个数量级,而且怎么调参都没用。

Cua 团队(就是开源 macOS 虚拟化栈 Lume 的那拨人)最近发布的研究找到了原因,也给出了解决方案:问题出在虚拟 GPU 上报的 Metal 能力太保守,导致 llama.cpp 选择了慢速内核路径。他们开源了一个进程级 Metal 能力 shim,实测让 TinyLlama 的提示处理速度提升 11 倍、token 生成提升 16 倍,Gemma 4 12B 分别提升 7.2 倍和 14.5 倍,接近裸机水平。

问题根源:虚拟 GPU 在"撒谎"

先理解机制。Apple 的 Virtualization.framework 会给 macOS 客户机提供一个虚拟图形设备(VZMacGraphicsDeviceConfiguration),客户机里的 Metal 工作通过专用驱动提交,宿主机栈在物理 GPU 上执行。这是典型的半虚拟化(paravirtualization)架构——宿主机掌握硬件,客户机用虚拟化感知的设备。

但问题在于:这台虚拟设备上报的 Metal 能力画像非常保守。在 Cua 的测试中,Tahoe(macOS 26)虚拟机的虚拟 GPU 上报的是"Apple 5 代"级别的能力:最大 threadgroup 内存只有 32 KB,SIMD-group matrix 支持标记为不可用。

Metal 应用(包括 llama.cpp)会通过运行时查询这些能力值来选择内核和渲染路径。既然虚拟设备说"我不支持新特性",llama.cpp 就老老实实走老的慢速路径——尽管物理 GPU 完全能跑新内核。Apple 官方文档也是这么建议的:查询设备能力,按能力选路径。应用只是在做平台告诉它该做的事。

顺带一提,这不是 Lume 独有。Tart(另一个 macOS 虚拟化 CLI)的 GitHub 上有一个长期未关闭的 issue,标题就叫"No GPU passthrough in macOS guest?",讨论的就是 macOS 客户机里的图形和 LLM 性能问题。

—— 广告 ——

解决方案:进程级 Metal 能力 shim

Cua 的思路不是去改虚拟化栈,也不是做真正的 GPU passthrough(在 macOS 上走 VFIO 那套不现实),而是插入一个兼容层,只改变单个客户机进程查询到的能力答案。

这个 shim 只做两件事:

能力默认客户机解锁后
supportsFamily:1009(Apple 9 代)falsetrue
SIMD-group matrix
SIMD-group reduction
bfloat16
最大 threadgroup 内存32 KB64 KB

改动范围被刻意收窄:只动 Apple family 枚举和 threadgroup 内存上限,公共 API、Metal 3、工作集大小等保持默认值。这样做的原因是踩过坑——在消融实验中,如果把 MTLGPUFamilyMetal3 也改成支持,MLX 会请求一个半虚拟化设备提供不了的内存驻留集,直接翻车。所以发布版 shim 严格限制改动范围。

工作负载仍然走 Apple 的 Virtualization.framework 图形路径,在宿主机的物理 GPU 上执行,只是能力查询的结果变了。shim 是进程级的,用 DYLD_INSERT_LIBRARIES 注入,只影响被注入的进程及其子进程。

实测数据:接近裸机

测试环境是 M1 Ultra(48 核 GPU)+ macOS 26.6.1,客户机是 Tahoe Cua 镜像(macOS 26.5.2,8 vCPU,16 GiB),跑在 Lume 0.5.1 里,llama.cpp 官方 b10167 版本。

TinyLlama 1.1B Chat Q4_K_M(基准命令 llama-bench -p 512 -n 128 -r 10 -t 8 -ngl -1):

工作负载裸机默认客户机解锁客户机提速裸机占比
提示处理(512 token)4872 tok/s432 tok/s4787 tok/s11.08×98.25%
token 生成(128 token)287 tok/s12.6 tok/s207 tok/s16.36×72.06%

Gemma 4 12B 指令版 QAT Q4_0 GGUF(6.98 GB,今年发布):

工作负载裸机默认客户机解锁客户机提速裸机占比
提示处理518 tok/s71.7 tok/s516 tok/s7.20×99.59%
token 生成52.4 tok/s3.41 tok/s49.7 tok/s14.54×94.82%

Meta 官方 Muse Glimmer 30B Q4_K-M GGUF(64 GiB 客户机,llama.cpp b10359):提示处理 25.8 → 195 tok/s(7.55×),token 生成 2.38 → 21.1 tok/s(8.87×)。

有意思的是 MLX-LM 的表现:它的速度在默认虚拟机里本来就很快(提示处理 1656 tok/s),解锁后基本没变化(1665 tok/s,1.005×)。这说明 MLX 已经能在保守能力下走高效的 Metal 路径,而 llama.cpp 则严重依赖新特性。这个对比也解释了为什么 shim 要谨慎——不是所有框架都需要它。

操作步骤

整个解锁流程分三步。

第一步:构建并验证 shim(在宿主机上):

code
cd libs/lume/metal-capability-shim
./Scripts/build.sh
./Scripts/verify.sh

源码在 libs/lume/metal-capability-shim,会编译出两个架构专用的 dylib(LumeMetalCapabilities-arm64.dylib 等)。

第二步:开启宿主机的非受限特性级别。停掉虚拟机,写入偏好设置,再重新启动:

code
lume stop my-vm
defaults write com.apple.gpusw.ParavirtualizedGraphics \
  ForceUnrestrictedDeviceFeatureLevel -bool true
lume run my-vm

第三步:把 dylib 复制进客户机,用 DYLD_INSERT_LIBRARIES 注入目标进程

code
lume ssh my-vm \
  "DYLD_INSERT_LIBRARIES=/path/to/LumeMetalCapabilities-arm64.dylib \
   LUME_METAL_APPLE_FAMILY_MAX=1009 \
   /path/to/metal-capabilities 1009"

对于长期运行的推理服务(llama-server、渲染进程等),建议用一个 per-workload 的 LaunchAgent 设置环境变量,这样登录会话本身保持默认状态。Cua 的 Lume 文档里有完整的 LaunchAgent 模板、校验和验证步骤和回滚说明。

回滚很简单:去掉环境变量重启工作负载就回到默认行为;要恢复宿主机偏好,停掉 VM、删除 ForceUnrestrictedDeviceFeatureLevel 键再启动即可。

注意事项与风险

这个方案有几个明确的边界,用之前心里要有数:

  • 实验性且版本敏感:shim 依赖客户机 Metal 实现的私有行为,macOS 任何一次更新都可能改掉。每个宿主机/客户机组合都要单独测试。
  • 进程级生效:只影响注入的进程及其子进程;hardened 或平台保护的二进制可能拒绝注入。
  • 能力画像有限:上报的是测试覆盖过的 Apple family 值,不代表物理 GPU 的完整能力;每个新 Metal API 都要单独验证。
  • 仍是虚拟机Virtualization.framework 本身的渲染和虚拟化限制依旧存在,token 生成也没到裸机 100%(TinyLlama 是 72%)。
  • 不是传统 GPU passthrough:物理 GPU 直通、PCI/VFIO、内核级修改都不在这个机制的范围内。

顺带提醒:TinyLlama 默认客户机 432 tok/s 的提示处理速度确实慢得离谱,如果只是偶尔在 VM 里跑小模型,可以先跑一次 llama-bench 对比宿主机速度,确认是不是同样的问题再上 shim。

对你有什么用

如果你在 macOS 虚拟机里跑 llama.cpp(本地 agent、开发调试、隔离测试),这个 shim 能直接把你从"慢到没法用"拉回"接近裸机"的水平——提示处理几乎无损(98-99%),生成速度至少提升到裸机的 7 成以上。对于 Gemma 4 12B 这类实际会用的模型,14.5 倍的生成提速意味着 3.4 tok/s 的"幻灯片模式"变成 49.7 tok/s 的流畅对话。

所有源码、构建脚本、能力探测工具和原始基准日志都在仓库里(evidence/lume-metal-capability-shim/ 目录),每个模型的测试都有完整的哈希校验和环境记录,想复现或者在自己机器上验证可以照着跑。

分享到
微博Twitter

© 2026 四月

原文链接:https://www.aprilzz.com/tutorials/macos-vm-gpu-passthrough