在 macOS 虚拟机上通过 GPU Passthrough 和 Llama.cpp 优化,Apple Silicon 实现最高 16 倍的 LLM 推理加速,详细技术方案见 GitHub 仓库。
Published on August 11, 2026 by Francesco Bonacci and Johnny Franks
如果你从一开始就在关注 Cua,可能记得它始于 Lume(我们的 macOS 虚拟化栈)的 Show HN 发布。
今天,我们分享一个更宏大计划的初步成果——将 Virtualization.framework 基础与 Cua Driver 的本地 computer-use 环境以及 Cua Cloud 和 Fleets 的基础设施连接起来:一个小型、进程范围的兼容层,可在 macOS guest 中解锁更新的 Metal 快速路径。
我们今天以与 Lume 和 Cua 相同的宽松许可证发布这项研究工作,以便他人复现结果,并帮助摸清哪些 Apple Silicon 芯片、macOS 版本和 Metal 工作负载能从中受益。

Apple Vz 用户在其他地方也遇到了这些限制。Tart 是另一个基于 Apple Virtualization.framework 构建的知名 CLI,有一个公开的"No GPU passthrough in macOS guest?"issue,询问该框架是否能在 macOS VM guest 中提供可用的图形和像样的 LLM 性能。VM 继续使用 Apple 提供的虚拟 GPU。我们的工作在该设备上暴露了更新的 Metal 路径,并缩小了部分实际差距。
在 M1 Ultra 上,通过 llama.cpp 运行的 TinyLlama 1.1B,处理 prompt 的速度比同款负载在同款标准 VM 中快 11.08 倍,生成 token 的速度快 16.36 倍。Prompt 处理达到了裸机结果的 98%。源码、构建脚本、能力探测工具和原始基准日志都已包含,以便你检查和复现结果。
我们用 Google 今年发布的 6.98 GB 模型 Gemma 4 12B QAT Q4_0 重复了实验。同样的层将 prompt 处理提升了 7.20 倍,token 生成提升了 14.54 倍。解锁后的 VM 达到了裸机 prompt 速度的 99.59% 和裸机生成速度的 94.82%。
macOS VM 中的天花板
Apple 的 Virtualization.framework 向 macOS guest 呈现一个虚拟图形设备。Guest 通过专用的 GPU 驱动提交 Metal 工作,Apple 的 host 栈在物理 GPU 上执行它。这种安排是半虚拟化(paravirtualization),host 保持对硬件的控制,而 guest 使用感知虚拟化的设备。
这与基于 QEMU 和 KVM 构建的其他虚拟化栈不同,它们可以使用不同的架构。在 x86 Linux host 上,VFIO 可以通过 IOMMU 将兼容的物理 PCI 设备或硬件功能分配给 VM,让 guest 直接访问该设备。这就是通常所说的 GPU passthrough 模型。
在我们标准的 Tahoe VM 中,半虚拟化设备报告大约是 Apple 5 时代的家族、32 KB 的最大线程组内存,以及 SIMD 组矩阵支持不可用。现代 Metal 软件用这些答案来选择内核,所以 llama.cpp 走了一条较慢的路径,尽管该设备可以执行更新的内核。
Apple 通过 GPU 家族和功能表记录 GPU 能力,并建议在运行时查询设备。这使得报告的能力边界变得有意义:应用程序的行为完全符合平台告诉它们的。

解决方案:一个进程范围的 Metal 能力垫片
我们构建了一个小型 Metal 能力垫片(一个插入应用程序和 API 之间的兼容层),运行在一个 guest 进程内部。它拦截选定的 Metal 能力查询,并更改返回给该进程的答案。Metal 应用程序用这些答案来选择内核,因此返回经过测试的 Apple 家族和线程组内存值可以让 llama.cpp 选择更新的 GPU 路径。对于我们测试的配置文件,垫片:
这足以让测试的 llama.cpp 构建选择更新的 SIMD 组归约、SIMD 组矩阵和 bfloat16 路径:
测试的配置文件更改了两个报告值:Apple 家族答案和线程组内存限制。Common、Mac、Metal 和 working-set-size 值在基准测试期间保持其标准设置。我们移除了原始研究钩子的私有 feature-profile 钩子、时钟和时间插值、网格替换、光线追踪覆盖、参数布局 guard 和管道编译回退。它的源码足够小,可以审查,格式错误或缺失的配置会使进程保持其标准能力路径。
工作负载留在 Apple 的 Virtualization.framework 图形路径上,并在 host 的 Apple GPU 上执行。能力更改仅限于注入的 guest 进程。
物理 GPU 分配、原始 PCI 或 VFIO passthrough 以及内核更改都在此机制之外。报告的家族描述了我们测试覆盖的路径;每个额外的 Metal API 需要单独的验证。
垫片在 Apple 现有的虚拟 GPU 路径上解锁 Metal 能力。VM 用户经常在"GPU passthrough"这个名称下遇到更广泛的限制。
来自最小构件的新结果
我们在配备 48 核 GPU 的 Apple M1 Ultra 和 macOS 26.6.1 上进行了测试。Guest 是当前的公共 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 -m tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf \
-p 512 -n 128 -r 10 -t 8 -ngl -1 -o json
下面的值是每次基准测试行发出的 10 个样本的中位数:
Prompt 处理几乎达到了 host 结果。生成达到了 host 速度的 72.06%,留下了可测量的 VM 差距。收益取决于 host GPU、guest 版本、应用程序和工作负载形态。
TinyLlama 原始结果和环境记录包含确切的镜像摘要、模型和二进制哈希、命令、JSON 输出、stderr 和校验和。这些候选发布结果认证了本文中使用的精简垫片。
TinyLlama 是一个有用的受控基准,因为它的运行速度很快,并且能清晰地暴露 Metal 路径。我们还想要一个开发者今天可能会选择的大型模型,所以我们通过相同的 llama.cpp 二进制运行了 Google 官方的 Gemma 4 12B 指令调优 QAT Q4_0 GGUF。
host、VM、垫片、基准测试形状和十样本方法保持不变。我们禁用了推测解码并保持多模态投影仪未加载,使比较保持在相同的 Metal 推理路径上:
Gemma 4 证据将 Google 的模型修订版和 SHA-256 固定在最终原始样本旁边。在检测到另一个 host 计算工作负载后,我们丢弃并重新运行了一个初步的标准系列。保留的标准、解锁和裸机文件来自同一个无争议窗口,并显示紧密的样本范围。
我们还使用 mlx-community/Llama-3.2-3B-Instruct-4bit 在 MLX 0.32.0 上测试了 MLX-LM 0.31.3。性能保持平稳,因为 MLX-LM 在标准 VM 中已经很快了:
这个平稳结果有助于定义发布配置文件。在消融过程中,广告 MTLGPUFamilyMetal3 使 MLX 请求通过半虚拟化设备无法提供的驻留集。发布垫片限制更改了 Apple 家族枚举的答案,并保持 Metal 3 为其标准值。相关的 MLX 分支在其 Metal 驻留实现中是可见的。
这在 Apple 平台中的位置
这完全通过 Apple 随 Virtualization.framework 提供的半虚拟化 GPU 路径在 Apple 硬件上运行。垫片影响一个 guest 进程读取的选定值。host、guest 内核、其他 guest 进程、内容保护状态和许可状态保持其现有配置。
该技术依赖于 guest Metal 实现中的私有、版本敏感的行为。Apple 可能会在 macOS 版本之间更改它,因此我们独立测试每个 host 和 guest 组合。不支持的方法使进程保持其标准路径,每个额外的 API 需要自己的虚拟化测试。
我们欢迎 Apple 就半虚拟化图形的无限制功能级别的预期行为和支持性提供澄清。从事 Metal 或 Virtualization.framework 工作的 Apple 工程师可以通过 vz@trycua.com 联系我们。
源码位于 libs/lume/metal-capability-shim。构建并验证两个架构特定的 dylib:
cd libs/lume/metal-capability-shim
./Scripts/build.sh
./Scripts/verify.sh
停止 VM,为你的 macOS 用户启动的 VM 启用无限制功能级别,然后重新启动:
lume stop my-vm
defaults write com.apple.gpusw.ParavirtualizedGraphics \
ForceUnrestrictedDeviceFeatureLevel -bool true
lume run my-vm
将匹配的 dylib 和探测工具或工作负载复制到 guest 中,然后将激活范围限定为该进程:
lume ssh my-vm \
"DYLD_INSERT_LIBRARIES=/path/to/LumeMetalCapabilities-arm64.dylib \
LUME_METAL_APPLE_FAMILY_MAX=1009 \
/path/to/metal-capabilities 1009"
对于长期运行的推理服务器、渲染器或 worker,使用 per-workload LaunchAgent。在该工作负载的环境中设置 DYLD_INSERT_LIBRARIES,以使登录会话保持标准配置。Lume 指南有完整的模板、校验和验证步骤和回滚说明。
移除环境变量并重新启动工作负载会将其恢复到标准行为。要恢复 host 首选项,停止 VM,删除 ForceUnrestrictedDeviceFeatureLevel,然后再次启动 VM。
实验性和版本敏感。垫片使用可能在任何 macOS 版本中更改的私有 guest Metal 实现细节。
Per-process。它仅影响注入的工作负载及其子进程;强化或平台保护的可执行文件可能拒绝库注入。
配置的能力配置文件。它报告了我们测试覆盖的 Apple 家族值。物理 GPU 能力发现不在其范围内。
狭窄的验证。当前的证据涵盖了列出的 M1 Ultra host 和 Tahoe guest 上的能力探测、两个 llama.cpp 工作负载和一个 MLX-LM 兼容性运行。额外的芯片、guest 版本、模型和 Metal API 需要单独的测试。
仍然是 VM。现有的 Virtualization.framework 渲染和虚拟化限制仍然存在。
Guest 的保守答案掩盖了一条能力惊人的 GPU 路径。在我们的测试机器上,两个范围狭窄的能力更改将 TinyLlama prompt 处理从 432 提升到 4,787 tokens/秒。使用 Gemma 4 12B,prompt 处理从 71.66 提升到 515.76 tokens/秒,生成从 3.41 提升到 49.67,同时工作负载保持在 Apple 现有的 GPU 桥接上。
Lume 最初是让 macOS VM 对开发者实用的一种方式。这个结果为我们提供了跨更多 Apple Silicon 代际、guest 版本和 Metal 工作负载进行测试的基础。
想帮忙吗?在 GitHub 上给 Cua 加星,并在你的设置上测试垫片。用你的 host 芯片、host 和 guest 版本、确切的工作负载以及标准和解锁结果开一个 issue。如果你验证了新的组合或改进了垫片,请发送 pull request。