MNN 与 ncnn 对照:学完 MNN 再读 ncnn 的桥梁
MNN 精读见 精读: mnn(用户正在学习) ncnn 精读见 精读: ncnn ncnn 贡献指南见 竞赛指南: ncnn
调研元信息
| 项 | 值 |
|---|---|
| 日期 | 2026-06-22 |
| 环境 | macOS / Apple Silicon |
| 实测状态 | 未实测,以已有精读 + 源码为准 |
| 流水线段 | Deploy/Run(两引擎内部机制对照) |
一句话定位
为正在学 MNN 的你架一座桥——把 MNN 里学到的概念一一映射到 ncnn 的对应设计,让你「学一个等于学两个」。
日常类比
MNN 是一辆「全功能房车」(能住能做饭能开),ncnn 是一辆「赛车」(极致轻量极致快,但只能开不能住)。两辆车的引擎原理相通(内燃机/SIMD),但设计哲学截然不同——理解房车的人再看赛车会觉得「原来可以这么简单」。
核心对照表
| 维度 | ncnn | MNN | 关键差异 |
|---|---|---|---|
| 内存布局 | elempack 动态打包(NEON=4, AVX=8, AVX-512=16) | NC4HW4 固定按 4 打包 | ncnn 按 SIMD 宽度自适应;MNN 固定 4,AVX-512 浪费 75% |
| 调度模型 | 按需递归(Lazy Eval),从输出反向触发 | Pipeline 四步(onResize + onExecute),正向全图调度 | ncnn 不用的层完全不执行;MNN 全图统一优化 |
| GPU 后端 | Vulkan Compute Shader 一套 GLSL | OpenCL + Metal + Vulkan + CUDA | ncnn 一套代码全平台;MNN 每平台最优但代码量大 |
| 模型转换 | PNNX(保留 PyTorch 语义)+ onnx2ncnn | MNNConvert(ONNX/TF/Caffe/TorchScript) | PNNX 更精确;MNNConvert 输入格式更多 |
| 图优化 | 极简(层融合为主,无独立 pass 系统) | Geometry Compiler(Op 降级为 Region 描述 + Raster 合并) | MNN 优化更深但复杂度更高 |
| LLM 推理 | 无(社区实验) | 内建 MNN-LLM(KV-Cache + 分词 + 采样) | MNN 独有优势 |
| 端侧训练 | 不支持 | 支持 | MNN 独有 |
| 代码量 | 约500 文件 | 约2000 文件 | ncnn 4x 更精简 |
| C++ 标准 | 不用 C++11 | C++14+ | ncnn 更保守 |
| 依赖 | 零第三方 | protobuf + flatbuffers 等 | ncnn 可嵌入任何环境 |
| 许可证 | BSD 3-Clause | Apache 2.0 | 均商业友好 |
概念映射详解
1. 内存布局:elempack vs NC4HW4
graph LR
subgraph ncnn[ncnn: elempack 动态]
A[检测 SIMD 宽度] --> B{ARM NEON?}
B -->|是| C[elempack=4]
B -->|否| D{AVX2?}
D -->|是| E[elempack=8]
D -->|否| F{AVX-512?}
F -->|是| G[elempack=16]
end
subgraph MNN[MNN: NC4HW4 固定]
H[始终 pack=4] --> I[NEON 刚好用满]
H --> J[AVX2 只用一半]
H --> K[AVX-512 只用1/4]
end
MNN 学习收获映射到 ncnn:你在 MNN 理解了「为什么要通道打包」(SIMD 一次处理多通道),再看 ncnn 时会发现它更进一步——运行时动态适配硬件宽度,但代价是内存管理更复杂。
2. 调度模型:Lazy Eval vs Pipeline
| MNN Pipeline 四步 | ncnn 对应 |
|---|---|
| onResize():分配内存 | 无对应,按需分配 |
| onExecute():执行计算 | Layer::forward():计算+分配一体 |
| Buffer 复用优化 | Light Mode:blob 消费即释放 |
| 全图正向调度 | 从 output blob 反向递归 |
MNN 学习收获:你理解了「分离内存分配和计算的好处」(onResize 提前规划内存复用)。ncnn 走另一条路——lazy eval 根本不给不需要的层分配内存。trade-off:MNN 全图可全局最优化内存复用;ncnn 更简单但可能局部次优。
3. GPU 后端:统一 vs 分治
| MNN 做法 | ncnn 做法 |
|---|---|
| OpenCL(安卓主力) | Vulkan(安卓 + macOS + Windows) |
| Metal(iOS 专属) | Vulkan 覆盖 iOS(有限支持) |
| CUDA(服务端扩展) | 无 CUDA |
| 每平台最优实现 | 一套 GLSL 全平台 |
MNN 学习收获:你理解了「为什么 GPU 编程要处理内存布局转换、命令队列同步」。ncnn 的 Vulkan 后端让你看到「如何用一套 Compute Shader 抽象跨所有 GPU」——但代价是某些平台可能不如 MNN 的 Metal/OpenCL 专属实现。
4. 模型转换:MNNConvert vs PNNX
| MNNConvert | PNNX |
|---|---|
| 输入:ONNX/TF/Caffe/TorchScript | 输入:TorchScript(优先),可出 ONNX |
| 输出:.mnn 单文件 | 输出:.param + .bin + Python 可读 .py |
| 特色:集成量化选项 | 特色:保留 PyTorch nn.Module 语义 |
| 可读性:二进制,需 viewer | 可读性:.param 人类可读文本 |
5. 图优化深度对比
graph TD
subgraph MNN[MNN Geometry Compiler]
M1[原始 Op 图] --> M2[非计算 Op 降级为 Region]
M2 --> M3[Region 合并为 Raster Op]
M3 --> M4[核心 Op 保留原生实现]
M4 --> M5[新后端只需实现核心 Op]
end
subgraph ncnn[ncnn 图优化]
N1[原始 Layer 图] --> N2[层融合: Conv+BN+ReLU]
N2 --> N3[直接执行]
end
MNN 的 Geometry Compiler 是「编译器级」优化——新增后端的工程量从 O(全部 Op) 降为 O(核心 Op)。ncnn 则采取「够用就好」——层融合解决 90% 性能问题,剩下的靠手写 SIMD/GLSL。
学完 MNN 再读 ncnn 的推荐路线
| 顺序 | ncnn 文件/目录 | 对应 MNN 知识 | 阅读目标 |
|---|---|---|---|
| 1 | src/mat.h + src/mat.cpp | MNN Tensor/Buffer | 理解 elempack 动态打包 |
| 2 | src/layer.h | MNN Op 基类 | 对比 forward vs onExecute |
| 3 | src/net.cpp (Extractor) | MNN Session/Pipeline | 理解 Lazy Eval 调度 |
| 4 | src/layer/convolution.cpp | MNN CPUConvolution | 对比 SIMD 优化实现 |
| 5 | src/gpu.h + layer/vulkan/ | MNN OpenCL/MetalBackend | 对比 GPU 抽象层 |
| 6 | tools/pnnx/src/ | MNN tools/converter/ | 对比转换链实现 |
预计每步 1-2 小时,总计 1 周可完成 ncnn 源码初览。
何时选 ncnn、何时选 MNN?
| 场景 | 推荐 | 理由 |
|---|---|---|
| 安卓 App 嵌入,追求最小体积 | ncnn | 约1MB,零依赖 |
| iOS App,追求 Metal 最优性能 | MNN | Metal 后端成熟 |
| 端侧 LLM / 大模型推理 | MNN | 内建 MNN-LLM |
| 犀牛鸟竞赛贡献(ncnn 赛道) | ncnn | 直接相关 |
| 需要端侧训练/fine-tune | MNN | 唯一支持者 |
| 跨平台 GPU 加速(含 Windows) | ncnn | Vulkan 全覆盖 |
| 纯 CPU 推理,YOLO 检测 | 两者皆可 | 性能接近,看熟悉度 |
证据等级
| 标签 | 来源 |
|---|---|
| [已有] | deep-dive-ncnn.md(elempack + Vulkan + Lazy Eval), deep-dive-mnn.md(Geometry + NC4HW4 + Pipeline) |
| [源码] | /tmp/ncnn src/, /tmp/MNN source/ |
| [文档] | ncnn wiki, MNN 官方文档 |