MNN 表达式模型性能评测实战:benchmarkExprModels 的构建、参数与源码级测量原理

发布时间:2026/9/14 22:59:42
MNN 表达式模型性能评测实战:benchmarkExprModels 的构建、参数与源码级测量原理 MNN 表达式模型性能评测实战benchmarkExprModels 的构建、参数与源码级测量原理【免费下载链接】MNNMNN: A blazing-fast, lightweight inference engine battle-tested by Alibaba, powering high-performance on-device LLMs and Edge AI.项目地址: https://gitcode.com/GitHub_Trending/mn/MNNMNN 仓库内置了一套不依赖外部模型文件、直接用 C 表达式Express API现场搭建经典视觉网络并进行推理打分的评测工具benchmarkExprModels。本文基于 benchmark/exprModels/Readme_CN.md 的完整操作流程结合 benchmark/benchmarkExprModels.cpp 与 benchmark/CMakeLists.txt 的源码实现讲解如何构建评测目标、如何命名与选择评测模型、每个命令行参数循环次数、forward type、线程数的准确含义以及底层 warmup、内存会话模式、精度配置等测量细节帮助读者在统一口径下横向对比 MNN 各后端的推理耗时。一、Expr Benchmark 的定位为什么用表达式建模来评测benchmark/目录下实际上包含两类评测入口都由 CMake 选项MNN_BUILD_BENCHMARK控制构建在 benchmark/CMakeLists.txt 中分别定义了两个可执行文件benchmark.out由 benchmark/benchmark.cpp 构建针对benchmark/models/目录下的现成.mnn模型文件如MobileNetV2_224.mnn、resnet-v2-50.mnn等打分适合对比真实转换后的模型benchmarkExprModels.out由 benchmark/benchmarkExprModels.cpp 与benchmark/exprModels/下的全部.cppfile(GLOB_RECURSE SRC_FILES .../exprModels/*.cpp)见 benchmark/CMakeLists.txt共同构建在代码里用MNN::Express表达式直接搭建 MobileNetV1/V2、ResNet、GoogLeNet、SqueezeNet、ShuffleNet 等网络结构再将其序列化为 FlatBuffer 模型交给 Interpreter 推理。Expr 方式的价值在于网络结构完全由源码决定形状、通道数、层数可参数化例如 ResNet 层数、MobileNet 宽度系数评测口径统一、可复现且无需下载任何模型文件。二、构建评测工具CMake 步骤按照 benchmark/exprModels/Readme_CN.md 给出的流程从 MNN 仓库根目录执行cd /path/to/MNN mkdir build cd build cmake -DMNN_BUILD_BENCHMARKtrue .. make -j8对应的实现依据根目录 CMakeLists.txt 定义了option(MNN_BUILD_BENCHMARK Build benchmark or not OFF)默认关闭必须像上面这样显式传入-DMNN_BUILD_BENCHMARKtrue或1、ON才会构建评测目标开启后CMake 流程会在 CMakeLists.txt 处进入IF(MNN_BUILD_BENCHMARK)分支并挂载 benchmark 子目录最终在build/下产出benchmark.out与benchmarkExprModels.out两个可执行文件Windows 下为.exe后缀。构建完成后可以查看帮助信息./benchmarkExprModels.out help三、命令行参数逐项解析Help 输出由 benchmark/benchmarkExprModels.cpp 中的_printHelp()打印定义的用法为Usage: benchmarkExprModels.out model_to_benchmark [loop_count] [forwardtype] [numberThread]结合 main 函数 的解析逻辑四个位置参数的确切含义如下位置参数默认值说明1model_to_benchmark无缺省打印 help 退出模型名命名规则见下一节也接受关键字default2loop_count10正式计分的推理循环次数每次耗时单独记录后统计 max/min/avg3forwardtype0CPU按MNNForwardType枚举的整数值解析static_castMNNForwardType(atoi(argv[3]))4numberThread4CPU 推理使用的线程数ScheduleConfig.numThreadforwardtype的取值来自 include/MNN/MNNForwardType.h0CPU、1Metal、2CUDA、3OpenCL、6OpenGL、7Vulkan、4AUTO等。需要注意的是forwardType() 这个回显函数只会把CPU / Vulkan / OpenCL / Metal四类翻译成可读名称其余取值显示为N/AGPU 后端能否真正生效还取决于编译时是否开启了相应后端支持因此对比 GPU 数据前先确认构建配置。文档给出的示例命令10 次循环、CPU 后端、4 线程./benchmarkExprModels.out MobileNetV1_100_1.0_224 10 0 4 ./benchmarkExprModels.out MobileNetV2_100 10 0 4 ./benchmarkExprModels.out ResNet_100_18 10 0 4 ./benchmarkExprModels.out GoogLeNet_100 10 0 4 ./benchmarkExprModels.out SqueezeNet_100 10 0 4 ./benchmarkExprModels.out ShuffleNet_100_4 10 0 4四、模型命名规则与可选取值模型名按_分割解析splitArgs见 benchmarkExprModels.cpp六个模型族的完整命名格式与合法取值如下全部可在 help 输出与对应头文件的枚举映射中得到印证模型族命名格式取值约束源码依据MobileNetV1MobileNetV1_{numClass}_{width}_{resolution}width ∈ {1.0, 0.75, 0.5, 0.25}resolution ∈ {224, 192, 160, 128}MobileNetExpr.hpp 中EnumMobileNetWidthTypeByString/EnumMobileNetResolutionTypeByStringMobileNetV2MobileNetV2_{numClass}—MobileNetExpr.hppResNetResNet_{numClass}_{layer}layer ∈ {18, 34, 50, 101, 152}ResNetExpr.hppGoogLeNetGoogLeNet_{numClass}—GoogLeNetExpr.hppSqueezeNetSqueezeNet_{numClass}—SqueezeNetExpr.hppShuffleNetShuffleNet_{numClass}_{group}group ∈ [1, 2, 3, 4, 8]ShuffleNet 的 group conv 分组数ShuffleNetExpr.hpp其中{numClass}即全连接分类层输出的类别数只影响最后一层1x1卷积的输出通道对整体耗时影响很小因此文档示例里取100即可。参数非法时程序会给出明确报错并退出例如 MobileNetV1 宽度非法时打印Only [1.0, 0.75, 0.5, 0.25] be support见 benchmarkExprModels.cppResNet 层数非法时打印Only [18, 34, 50, 101, 152] be support。另外第一个参数传default时会批量跑一组 1000 类的标准模型清单定义在 gDefaultModelsstatic std::vectorstd::string gDefaultModels { MobileNetV1_1000_1.0_224, MobileNetV2_1000, GoogLeNet_1000, ShuffleNet_1000_4, SqueezeNet_1000, ResNet_1000_18, ResNet_1000_50, };各模型的原始论文引用直接写在了对应头文件注释里例如 benchmark/exprModels/MobileNetExpr.hpp 标注了 MobileNet 与 MobileNetV2 两篇 paper 链接ResNetExpr.hpp、ShuffleNetExpr.hpp、GoogLeNetExpr.hpp、SqueezeNetExpr.hpp 同理原文档中也提示相应模型的 paper 链接附在头文件里。五、测量机制源码剖析warmup、会话模式与统计口径理解输出数字的前提是看清 runNet() 的完整流程表达式 → 模型文件Variable::save({netOutput}, netTable)把表达式图保存为NetT结构体再用flatbuffers::FlatBufferBuilderCreateNet序列化成 FlatBuffer 字节流随后Interpreter::createFromBuffer(buf, size)从内存直接创建解释器——全程不落地磁盘文件低内存会话模式net-setSessionMode(Interpreter::Session_Release)之后createSession(config)并立即net-releaseModel()即释放常驻模型、只保留运行时必需的内存更接近端侧真实部署的内存形态输入填充从 session 输入创建 host tensor把全部元素置 0 后copyFromHostTensor写回设备每次计时迭代都会重新拷入输入保证计时覆盖完整的输入拷贝 → 推理 → 输出拷出链路warmup 3 次正式循环前先空跑 3 次// Warming up...让页表、内存分配与可能的编译期准备稳定下来计时循环getTimeInUs()POSIX 下基于gettimeofdayWindows 下基于QueryPerformanceCounter见 benchmarkExprModels.cpp记录每次迭代耗时换算成毫秒存入costs。最终由 displayStats() 打印单行结果形如[ - ] MobileNetV1_100_1.0_224 max 12.345ms min 9.876ms avg 10.543ms数值为格式示意实际数据以本机运行为准。还有一个容易忽略的点main中构造的ScheduleConfig固定设置了 后端配置BackendConfig bnConfig; bnConfig.precision BackendConfig::Precision_Low; // 低精度允许 float16 bnConfig.power BackendConfig::Power_High; // 高功耗档 config.type forward; config.numThread numThread;也就是说 expr benchmark 默认在Precision_Low Power_High配置下测量这是 MNN 面向移动端性能优先的典型调法如果你要评估精度/性能折中或省电场景下的耗时需要自行修改这段配置再重新构建。六、表达式建模示例MobileNetV1 是如何用 Express API 搭出来的benchmark/exprModels/MobileNetExpr.cpp 展示了典型的表达式建模方式。以mobileNetV1Expr为例输入用_Input({1, 3, inputSize, inputSize}, NC4HW4)创建——注意直接使用 MNN 的NC4HW4数据排布而非NCHW这避免了评测中额外引入一次 layout 变换测的是后端对原生排布的真实吞吐宽度系数映射为首层通道数MobileNet_100 → 32、075 → 24、050 → 16、025 → 8后续通道逐层×2见 MobileNetExpr.cpp每个残差块convBlock由3x3 深度可分离卷积group 输入通道数 1x1 逐点卷积两个_Conv组成见 MobileNetExpr.cpp对应论文中 depthwise separable convolution 的实现注释里还提到 MNN 转换器对转换模型会把DepthwiseConvBNRelu融合为纯卷积形式与这里的表达式写法保持一致收尾是全局_AvePool1x1卷积充当全连接层// reshape FC with Conv1x1_Softmax见 MobileNetExpr.cpp。ResNet、GoogLeNet 等其余模型也是同样的组织方式每个模型族一个XxxExpr.hpp/.cpp入口函数统一返回MNN::Express::VARP网络输出再聚合进 ExprModels.hpp 供 main 分发。若要新增一个模型参与 expr benchmark按这个模式添加头文件、实现函数并在 benchmarkExprModels.cpp 的for (auto model : models)分支里注册解析逻辑即可此处仅说明扩展思路仓库为只读实际修改请在自己的 fork 中进行。七、扩展到真实模型与跨框架对比脚本Expr benchmark 回答同一结构下 MNN 各后端跑得快不快而 benchmark/scripts/ 目录提供了面向真实转换模型的评测链路benchmark/models/下预置了MobileNetV2_224.mnn、inception-v3.mnn、mobilenet-v1-1.0.mnn、nasnet.mnn、resnet-v2-50.mnn、squeezenetv1.1.mnn等现成 MNN 模型配合benchmark.out使用benchmark/scripts/convert.sh 与 benchmark/scripts/convert.py 负责把原始模型批量转换为 MNN 格式benchmark/scripts/bench.sh 与 benchmark/scripts/bench_pc.py 支持-f {mnn,tf,torch}在同一 PC 环境下循环推理并写入result/下的日志--loop-num、--thread-num、--backend {cpu,cuda}等参数见 bench_pc.py其中 MNN 一侧走ModuleBasic工具按input.json中声明的输入 shape/输出层执行历史评测结果记录在 benchmark/result/如 benchmark/result/2020-3-22.md可作为该评测体系的输出样例。两条路径配合使用先用 expr benchmark 做结构级、参数化的快速回归再用 scripts 链路对具体业务模型做绝对耗时验证。八、总结与注意事项构建必须显式开启-DMNN_BUILD_BENCHMARKtrue产物为benchmark.out文件模型与benchmarkExprModels.out表达式模型两个工具命令格式模型名 [loop_count] [forwardtype] [numberThread]默认 10 次循环、CPU、4 线程模型名遵循MobileNetV1_{类数}_{宽度}_{分辨率}等六族规则非法取值会被枚举校验直接拒绝测量口径为FlatBuffer 内存建模型 Session_Release低内存会话 3 次 warmup Precision_Low/Power_High后端配置每次迭代覆盖输入拷贝到输出拷出的完整链路输出 max/min/avg 三项毫秒级指标换后端对比时先确认编译时已启用对应 GPU 后端并以相同 loop 数、线程数与输入规模控制变量每个模型族对应的论文引用保存在各*Expr.hpp头部注释中可直接溯源到 MobileNet、ResNet、GoogLeNet、SqueezeNet、ShuffleNet 的原始论文。【免费下载链接】MNNMNN: A blazing-fast, lightweight inference engine battle-tested by Alibaba, powering high-performance on-device LLMs and Edge AI.项目地址: https://gitcode.com/GitHub_Trending/mn/MNN创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考