Apple Silicon 与 macOS 虚拟机:借助 Llama.cpp 实现 11–16 倍的 LLM 推理加速
研究团队为 macOS 虚拟机中的 Metal 能力查询构建进程级兼容层,使 llama.cpp 能选用更新的 Metal 内核。在 M1 Ultra 上,TinyLlama 1.1B 的提示处理速度提升 11.08 倍、token 生成提升 16.36 倍,接近裸机性能的 98%;Gemma 4 12B 的提示处理与生成速度分别提升 7.20 倍和 14.54 倍。
通过进程级 Metal 能力注入,macOS 虚拟机中 llm 推理速度提升一个数量级,为在隔离环境运行大模型提供了此前缺失的算力基础。
Apple Silicon 与 macOS 虚拟机:借助 llama.cpp 实现 11–16 倍更快的 LLM 推理
发布于 2026 年 8 月 11 日,作者 Francesco Bonacci 和 Johnny Franks
如果你从一开始就关注 Cua,你可能还记得它最初是以 Show HN 的形式发布的,介绍的是我们的 macOS 虚拟化技术栈 Lume。
通过 Apple 的 Virtualization.framework 运行的 macOS 客户机,使用的是由宿主机 Apple GPU 支持的虚拟 GPU。在我们原版的 Tahoe 虚拟机中,该设备报告的是一个较为保守的 Metal 能力配置。应用程序会依据这些返回结果来选择内核和渲染路径,这就导致 llama.cpp 运行的是慢得多的 GPU 代码。
我们构建了一个小型的、进程作用域的兼容层,它会针对某个客户机进程更改选定的能力返回结果,从而让 llama.cpp 能够选择更新的 Metal 内核。这是我们更宏大工作所取得的第一个成果——将 Lume 的虚拟化基础与 Cua Driver 背后的本地计算机使用环境,以及 Cua Cloud 和 Fleets 背后的基础设施连接起来。
我们今天将这项工作作为研究版本发布,采用与 Lume 和 Cua 相同的宽松许可证,以便其他人能够复现这些结果,并帮助厘清哪些 Apple Silicon 芯片、macOS 版本和 Metal 工作负载能够从中受益。
在 M1 Ultra 上,通过 llama.cpp 运行的 TinyLlama 1.1B 处理提示词的速度快了 11.08 倍,生成 token 的速度快了 16.36 倍,相比同一台虚拟机中的相同工作负载。提示词处理速度达到了我们裸机结果的 98%。源码、构建脚本、能力探测工具以及原始基准测试日志均已包含在内,方便你检查和复现这一结果。
我们用 Google 的Gemma 4 12B QAT Q4_0重复了该实验,这是一个今年发布的 6.98 GB 模型。同样的层将提示词处理速度提升了7.20 倍,token 生成速度提升了14.54 倍。解锁后的虚拟机达到了裸机提示词速度的 99.59% 和裸机生成速度的 94.82%。
同样的能力差距也出现在其他Virtualization.framework前端中。Tart,另一个 macOS 虚拟化 CLI,有一个未解决的“macOS 客户机中没有 GPU 直通?”issue,涵盖了 macOS 客户机内的图形和 LLM 性能问题。
macOS 虚拟机内部的上限
Apple 的Virtualization.framework向 macOS 客户机呈现一个虚拟图形设备。客户机通过专门构建的 GPU 驱动提交 Metal 工作,Apple 的主机栈在物理 GPU 上执行它。这种安排是半虚拟化,即主机保持对硬件的控制,客户机使用一个感知虚拟化的设备。
这与其他基于 QEMU 和 KVM 构建的虚拟化栈不同,后者可以使用不同的架构。在 x86 Linux 主机上,VFIO 可以通过 IOMMU 将兼容的物理 PCI 设备或硬件功能分配给虚拟机,让客户机直接访问该设备。这就是通常所说的 GPU 直通模式。
在我们原版的 Tahoe 虚拟机中,半虚拟化设备报告的大致是 Apple 5 时代的家族、32 KB 的最大线程组内存,并且 SIMD 组矩阵支持报告为不可用。现代 Metal 软件会依据这些返回值来选择内核,因此即便该设备能够执行更新的内核,llama.cpp 仍然走了较慢的路径。
Apple 通过 GPU 家族和功能表 来记录 GPU 能力,并建议在运行时查询设备。这使得所报告的能力边界变得至关重要:应用程序所做的正是平台告诉它们去做的事情。
解决方案:进程级 Metal 能力垫片
我们构建了一个小型 Metal 能力垫片(一种插入应用程序与 API 之间的兼容层),它在单个客户机进程内运行。它会拦截选定的 Metal 能力查询,并更改返回给该进程的答案。Metal 应用程序会依据这些答案来选择内核,因此返回经过测试的 Apple 家族和线程组内存值,就能让 llama.cpp 选择其更新的 GPU 路径。对于我们测试的配置,该垫片:
- 返回
supportsFamily:直至 Apple 家族 9(1009);以及 - 将报告的最大线程组内存从 32 KB 提升至 64 KB。
这足以让所测试的 llama.cpp 构建选择更新的 SIMD-group reduction、SIMD-group matrix 和 bfloat16 路径:
| 能力 | 默认客户机 | 测试配置 |
|---|---|---|
supportsFamily:1009 | false | true |
| SIMD-group matrix | off | on |
| SIMD-group reduction | off | on |
| bfloat16 | off | on |
| 最大线程组内存 | 32 KB | 64 KB |
所测试的配置档案更改了两个上报值:Apple 系列答案和线程组内存限制。在基准测试期间,Common、Mac、Metal 和工作集大小等值保持其出厂设置。我们移除了原始研究钩子的私有功能配置档案钩子、时钟与时序介入、网格替换、光线追踪覆盖、参数布局防护以及管线编译回退。其源代码小到足以审计,且配置格式错误或缺失时,进程会保持在出厂能力路径上。
该工作负载保持在 Apple 的 Virtualization.framework 图形路径上,并在宿主机的 Apple GPU 上执行。能力变更的作用范围仅限于注入的客户进程。
物理 GPU 分配、原始 PCI 或 VFIO 直通以及内核更改均不在此机制范围内。所上报的系列描述了我们的测试所覆盖的路径;每增加一个 Metal API 都需要单独验证。
该垫片在 Apple 现有的虚拟 GPU 路径上解锁了 Metal 能力。虚拟机用户通常以“GPU 直通”这一名称遇到更广泛的限制。
来自最小产物的最新结果
我们在配备 48 核 GPU 和 macOS 26.6.1 的一台 Apple M1 Ultra 上进行了测试。客户机是当前公开的 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
以下数值是每个基准测试行所输出的十个样本的中位数:
| 工作负载 | 裸机宿主机 | 原版客户机 | 解锁的客户机 | 客户机加速比 | 解锁后 / 宿主机 |
|---|---|---|---|---|---|
| 提示词处理,512 tokens | 4,871.99 tok/s | 431.86 tok/s | 4,786.70 tok/s | 11.08× | 98.25% |
| Token 生成,128 个 token | 286.71 tok/s | 12.63 tok/s | 206.60 tok/s | 16.36× | 72.06% |
提示词处理几乎达到了宿主机的结果。生成速度达到了宿主机速度的 72.06%,留下了可测量的虚拟机差距。这一提升取决于宿主机的 GPU、客户机版本、应用程序以及工作负载形态。
TinyLlama 原始结果与环境记录包含确切的镜像摘要、模型与二进制文件哈希、命令、JSON 输出、stderr 以及校验和。这些候选发布版结果验证了本文所使用的精简 shim。
一个当前的 12B 模型
TinyLlama 是一个有用的受控基准,因为它运行速度快,并且能清晰地展现 Metal 路径。我们还想要一个开发者如今可能会选择的大型模型,因此我们通过同一个 llama.cpp 二进制文件运行了 Google 官方的 Gemma 4 12B 指令微调 QAT Q4_0 GGUF。
宿主、VM、shim、基准测试形态以及十次采样的方法均保持不变。我们禁用了投机解码,并让多模态投影器保持未加载状态,从而将对比保持在同一条 Metal 推理路径上:
| 工作负载 | 裸金属宿主 | 原版 guest | 解锁版 guest | Guest 加速比 | 解锁版 / 宿主 |
|---|---|---|---|---|---|
| 提示词处理,512 tokens | 517.88 tok/s | 71.66 tok/s | 515.76 tok/s | 7.20× | 99.59% |
| Token 生成,128 tokens | 52.38 tok/s | 3.41 tok/s | 49.67 tok/s | 14.54× | 94.82% |
Gemma 4 证据将 Google 的模型修订版本和 SHA-256 与最终原始样本一并固定下来。在检测到另一个主机计算工作负载后,我们丢弃并重新运行了一组初步的原生系列测试。保留下来的原生、解锁和裸金属文件来自同一个无争用窗口,样本区间非常紧凑。
我们还测试了 MLX-LM 0.31.3 搭配 mlx-community/Llama-3.2-3B-Instruct-4bit 在 MLX 0.32.0 上的表现。性能持平,因为 MLX-LM 在原生虚拟机中本来就已经很快:
| 工作负载 | 原生客户机 | 解锁客户机 | 比值 |
|---|---|---|---|
| 提示词处理,512 tokens | 1,656.55 tok/s | 1,665.47 tok/s | 1.005× |
| Token 生成,128 tokens | 172.09 tok/s | 170.86 tok/s | 0.993× |
这个持平的结果帮助确定了发布配置。在消融实验中,启用广告 MTLGPUFamilyMetal3 使 MLX 请求一个无法通过半虚拟化设备获得的驻留集。发布用的 shim 将变更后的应答限制为 Apple 系列枚举值,并将 Metal 3 保持在其默认值。相关的 MLX 分支可在其 Metal 驻留实现中查看。
这与 Apple 平台的关系
这一切完全运行在 Apple 硬件上,通过 Apple 随 Virtualization.framework 提供的半虚拟化 GPU 路径实现。该 shim 只影响某一个 guest 进程读取的特定值。宿主机、guest 内核、其他 guest 进程、内容保护状态以及授权状态均保持其现有配置。
该技术依赖于 guest 的 Metal 实现中私有的、对版本敏感的行为。Apple 可能会在 macOS 各版本之间更改这一行为,因此我们独立测试每一种宿主机与 guest 的组合。不受支持的方法会让进程保持在其默认路径上,而每增加一个 API 都需要单独进行虚拟化测试。
我们欢迎 Apple 就半虚拟化图形不受限功能级别的预期行为与可支持性作出澄清。从事 Metal 或 Virtualization.framework 的 Apple 工程师可通过 vz@trycua.com 联系我们。
在 Lume VM 中试用
源码位于 libs/lume/metal-capability-shim。构建并验证两个架构专属的 dylib:
cd libs/lume/metal-capability-shim
./Scripts/build.sh
./Scripts/verify.sh停止虚拟机,为你的 macOS 用户启动的虚拟机启用不受限的功能级别,然后重新启动它:
lume stop my-vm
defaults write com.apple.gpusw.ParavirtualizedGraphics \
ForceUnrestrictedDeviceFeatureLevel -bool true
lume run my-vm将匹配的 dylib 以及探针或工作负载复制到客户机中,然后将激活范围限定到该进程:
lume ssh my-vm \ "DYLD_INSERT_LIBRARIES=/path/to/LumeMetalCapabilities-arm64.dylib \ LUME_METAL_APPLE_FAMILY_MAX=1009 \ /path/to/metal-capabilities 1009"
对于长时间运行的推理服务器、渲染器或工作进程,请使用按工作负载配置的 LaunchAgent。在该工作负载的环境中设置 DYLD_INSERT_LIBRARIES,以便登录会话保持原样。Lume 指南 提供了完整的模板、校验和验证步骤以及回滚说明。
移除环境变量并重新启动工作负载,即可恢复其原有行为。要恢复宿主机偏好设置,请停止虚拟机,删除 ForceUnrestrictedDeviceFeatureLevel,然后再次启动虚拟机。
局限性
- 实验性且对版本敏感。该 shim 使用了客户机 Metal 的私有实现细节,这些细节可能在任何 macOS 版本中发生变化。
- 按进程生效。它仅影响被注入的工作负载及其子进程;经过加固或受平台保护的可执行文件可能会拒绝库注入。
- 已配置的能力档案。它报告的是我们测试所覆盖的 Apple 系列取值。物理 GPU 能力发现仍不在其范围之内。
- 验证范围有限。当前证据仅覆盖能力探测、两个 llama.cpp 工作负载,以及在所列的 M1 Ultra 主机和 Tahoe 客户机上的一次 MLX-LM 兼容性运行。其他芯片、客户机版本、模型和 Metal API 都需要单独测试。
- 仍然是虚拟机。现有的
Virtualization.framework渲染和虚拟化限制依然存在。
总结
客户机给出的保守答案,掩盖了一条能力出人意料强大的 GPU 路径。在我们的测试机器上,两处范围很窄的能力改动,将 TinyLlama 的提示词处理从 432 tokens 每秒提升到 4,787 tokens 每秒。使用 Gemma 4 12B 时,提示词处理从 71.66 提升到 515.76 tokens 每秒,生成从 3.41 提升到 49.67,而工作负载始终运行在 Apple 现有的 GPU 桥接之上。
Lume 最初是为了让 macOS 虚拟机对开发者变得实用。这一成果为我们提供了一个基础,可以在更多 Apple Silicon 世代、客户机版本和 Metal 工作负载上进行测试。
想帮忙?在 GitHub 上给 Cua 点星,并在你的环境中测试这个 shim。提交一个 issue,附上你的主机芯片、主机和客户机版本、确切的工作负载,以及原版和解锁后的结果。如果你验证了新的组合或改进了这个 shim,发送一个 pull request。
来源:Hacker News 热门(buzzing.cc 中文翻译) · github.com