mllm:把多模态推理塞进手机和 NPU 的 C++ 引擎
Fast Multimodal LLM on Mobile Devices
秒懂
- 它是什么?
- mllm 是面向移动端与边缘设备的轻量多模态 LLM 推理引擎,用 Python 式即时执行写模型、用统一 IR 对接 Arm CPU、OpenCL、QNN 与 Ascend NPU。它的价值集中在量化部署与异构后端,代价是版本更迭快、API 仍在移动。
- 适合谁用?
- 如果你要在 Arm CPU、Hexagon NPU 或 Ascend NPU 上跑量化后的多模态模型,并且团队能接受跟着 v2 分支走,mllm 值得先做一次端到端验证;如果你的目标是桌面或服务器上的通用文本推理,llama.cpp 的成熟度和社区资料更省事,不必为此引入一套新的 IR 和转换工具链。上手前先确认三件事:你需要的模型是否出现在 v2 支持表里且带对应的量化版本,目标 SoC 的后端是标为可用还是仍在开发中,以及 mllm-convertor 能否吃下你手上的 PyTorch 或 SafeTensors 权重。
- 能商用吗?
- 可以。MIT 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
- 还在维护吗?
- 在维护。仓库最近一次提交在 7 天前。
- 用什么语言写的?
- 主要是 C++(依据 GitHub 的语言统计)。
以上回答依据项目的 GitHub 数据(最近同步于 2026年9月15日)和我们的分析,不构成法律意见。
开源项目深度解析
mllm 要解决的是端侧多模态的部署断层
端侧跑多模态模型,麻烦不在模型本身,而在从 PyTorch 权重到手机上可执行图之间那段路。视觉编码器、文本 token 预填充、解码三个阶段对算力的需求完全不同,量化位宽的选择也不一样,而不同厂商的 NPU 又各有自己的图格式和算子约束。mllm 的定位就落在这段路上:向上接住 Speculative Decoding、Pruning、Quantization 这类优化算法,向下对接 CANN、CUDA、MLIR 这些编译器与运行时层。README 里把这一层画成一张栈图,mllm 处在算法与硬件之间。
目标用户因此相当明确:需要把 Qwen3、Qwen3-VL、MiniCPM5-2B 这类模型塞进 Android 手机、Jetson Orin 或 Ascend 设备的人。如果你只是想在带独显的机器上跑一个聊天模型,这套东西的抽象层对你没有收益,反而多了一层需要维护的转换步骤。
即时执行与统一 IR:v2 换掉的不只是 API
v1 到 v2 的改动集中在模型编写方式。README 的 2025 年 8 月公告写明,v1 即将停止支持,v2 带来的是更 Pythonic 的即时执行(eager execution)写法、面向 NPU 集成的编译支持、多模型并行执行,以及一次工程实现上的重写。这四条里,前两条决定了你写模型的方式,后两条决定了你能在什么硬件上跑。
即时执行意味着模型结构在 Python 侧直接展开,而不是先落成一份静态图再交给运行时。对调试友好,对部署则是另一回事:真正上设备时仍然要经过 mllm-convertor 把 PyTorch 和 SafeTensors 检查点转成 mllm 格式,再由 mllm Runtime 加载执行。也就是说,开发期的即时执行和部署期的转换后执行是两条路径,README 的工作流图把它们画成前后衔接的两段,但文档没有说明两者在算子覆盖上是否完全一致。这是采用前值得自己验证的一点。
多模型并行执行是 v2 的另一个卖点。在端侧场景里,这个能力通常对应视觉编码器和语言模型分别占用不同后端的情况,但仓库材料没有给出具体的调度策略,只能确认该能力被列为 v2 的新特性。
后端矩阵:从 Arm CPU 到 QNN 与 Ascend
mllm 声称统一支持 Arm CPU、OpenCL GPU、QNN NPU 和 Ascend NPU 四类后端。真正决定能不能用的是支持表,而不是这行概述。
v2 的支持表按模型和设备逐格标注。Qwen3-0.6B 在 CPU 上是 w4a8 量化版本,在 Ascend NPU 上是 W8A8;Qwen3-1.7B 在 CPU 上是 w4a8-i8mm-kai,在 Hexagon NPU 上提供 Qnn-AOT-SM8650 的 W4A16 版本;Qwen3-4B 目前只列了 CPU 的 w4a8。表格只列到 Qwen3.5-0.8B 就被截断,后续条目无法从现有材料确认。
这里的模式很清楚:CPU 路径走得最远,量化版本最全;NPU 路径按芯片型号逐个适配,SM8650 是一个明确的目标,换一颗芯片就要重新确认。README 在 2026 年 2 月提到 Qnn AOT 已支持 NPU 上的全图执行,并给了快速上手页和技术报告的链接。全图执行的意义在于减少 CPU 与 NPU 之间的往返,但具体收益数字材料中没有给出,不要替它补。
Android 端不再走 JNI:端内 Golang 服务层
Android 实现被重构成端内的客户端-服务器结构,这一层用 Golang 写成,产物是 mllm_server.aar。README 明确说这与传统 JNI 集成不同,目的是把 UI 和沉重的推理计算解耦。
拆开看的收益是:推理进程崩溃不会直接带走界面,流式输出可以走本地 socket 而不是 JNI 回调,多模型并行执行也有了进程级的隔离位置。代价同样直接,多了一个 Go 运行时和一层 IPC,安装包体积和启动延迟都会受影响,调试时也要同时看 Java/Kotlin、Go 和 C++ 三层。README 没有给出这层 IPC 的开销数据。
2025 年 11 月的更新提到,Android Demo 通过这套端内服务架构实现了 Qwen3 和 DeepSeek-OCR 的稳定流式输出。这是仓库给出的最新状态描述,不是性能结论。
Jetson 上的数字怎么读
README 里最具体的一组数据来自 pymllm 在 Jetson Orin 上的表现,测试条件是 input_len=2048、output_len=128。Qwen3-VL-2B 的 W8A8 路径在 AGX Orin 32GB 上相对 llama.cpp 的预填充最高加速 3.12 倍,预填充吞吐约 12243 tok/s。仓库同时说明解码吞吐与 llama.cpp 大体接近,会因模型、设备和量化方式出现小幅胜负。
多模态预填充的表用的是另一套口径:bench_one_batch --image 测量视觉编码加图文 token 预填充的完整路径,单位是多次运行的平均 TPS。AGX Orin 32GB 上 Qwen3-VL-2B 的 FP16 为 4875.75、W4A16 为 4700.28、W8A8 为 6443.59;Qwen3-VL-4B 的 W4A16 为 2499.46、W8A8 为 3837.07。Orin NX 16GB 上对应数字整体更低,Qwen3-VL-2B 的 FP16 为 2438.27、W4A16 为 2494.89、W8A8 为 3200.40。
值得注意的是 W8A8 在这张表里全面领先 FP16,而 W4A16 相对 FP16 几乎没有优势,2B 模型在两个设备上都只差几十 TPS。这说明在这条路径上,权重量化到 4 bit 并没有换来吞吐,激活量化到 8 bit 才是有效的那一步。如果你按惯例默认 W4A16 最省,这张表给出了相反的信号。这些数字来自仓库自述的基准,不是第三方复现结果。
限制、失败模式与不适用场景
第一个限制是版本断层。README 写明 v1 支持即将结束,v2 在独立分支上。任何基于 v1 API 写下的集成代码都要重写,而 v2 的文档仍在补齐过程中。对已经上线的项目,这不是一次小升级。
第二个限制是后端成熟度不均。CUDA on Jetson Orin 和 Jetson Thor 在 2026 年 3 月被标注为实验性、仍在积极开发。实验性意味着接口和行为都可能变,把它放进产品路径需要自己承担返工风险。
第三个限制是模型覆盖。支持表按模型逐个列出,没有的模型就是没有。表格在 Qwen3.5-0.8B 处被截断,完整清单要以仓库当前页面为准。MiniCPM5-2B 在 2026 年 9 月加入,走的是 Arm CPU 推理。
第四个限制是转换链。mllm-convertor 负责把 PyTorch 和 SafeTensors 检查点量化并转成 mllm 格式,这一步失败或者算子不匹配,后面所有优化都无从谈起。材料中没有给出转换器的算子覆盖清单。
什么情况下不该用它:只需要在 x86 服务器或桌面上跑文本模型,llama.cpp 的成熟度和资料量都更合适;需要频繁更换模型架构做研究,每次都要走一遍转换流程会拖慢迭代;目标 NPU 不在支持表内,适配成本无法预估。
和 llama.cpp 的差别在转换链与硬件层
README 自己把 llama.cpp 当作 Jetson 上的对照基线,这个对照关系值得展开。两者的推理内核目标相近,差别在上下游。
llama.cpp 的重心是 GGUF 这一种格式加一套相对固定的量化方案,从 HuggingFace 权重到可运行文件之间的步骤少,跨平台一致性高。mllm 的重心是 mllm-convertor 加 mllm Runtime 这条链,中间多了一层自有格式,换来的是可以把图交给 QNN 或 Ascend 的编译器做全图执行,以及在 Android 上用端内服务层做进程隔离。
换句话说,llama.cpp 优化的是从权重到能跑的距离,mllm 优化的是从能跑到跑在 NPU 上的距离。仓库给出的预填充加速数据支持的正是后一种场景。如果你的设备只有 CPU,这条链多出来的抽象层不会带来对应收益。
维护成本与许可边界
维护成本主要来自三处。一是跟随 v2 分支,v1 退役后旧集成必须迁移。二是后端矩阵会随芯片和模型变化,支持表里的对勾是某个时间点的状态,不是长期承诺。三是转换工具链和运行时需要同步升级,两者版本错配时排查成本较高。
项目采用 MIT 许可,允许商用和修改,具体条款以仓库中的 LICENSE 文件为准,这里不构成法律意见。需要注意的边界在权重侧:支持表里链接的量化模型托管在 ModelScope 和 HuggingFace 上,它们各自的许可与 mllm 的 MIT 无关,商用前要单独确认。
发布节奏上,1.0.0 在 2024 年 1 月,2.0.0 在 2026 年 2 月,中间两年只有一次大版本。但 README 的更新日志显示 2026 年 3 月到 9 月之间持续有后端和模型支持上的提交,说明开发活跃度集中在小步迭代而非版本发布。这意味着你依赖的是 main 分支的当前状态,而不是一个稳定的 tag。
编辑结论
如果你要在 Arm CPU、Hexagon NPU 或 Ascend NPU 上跑量化后的多模态模型,并且团队能接受跟着 v2 分支走,mllm 值得先做一次端到端验证;如果你的目标是桌面或服务器上的通用文本推理,llama.cpp 的成熟度和社区资料更省事,不必为此引入一套新的 IR 和转换工具链。上手前先确认三件事:你需要的模型是否出现在 v2 支持表里且带对应的量化版本,目标 SoC 的后端是标为可用还是仍在开发中,以及 mllm-convertor 能否吃下你手上的 PyTorch 或 SafeTensors 权重。这三项里任何一项落空,后面调 kernel 的时间都白花。
社区笔记