ONNX导出必须在Python中完成,C++仅用于加载ONNX、构建ICudaEngine并执行推理;trtexec是首选转换工具,动态输入需显式指定optShapes,engine序列化加载更稳定,binding须按名匹配而非序号。

直接说结论:你不能用 C++ “导出”模型,torch.onnx.export 这类操作必须在 Python 环境中完成;C++ 的角色是加载 ONNX、构建 ICudaEngine、执行推理——这是两个阶段,混在一起必然踩坑。
ONNX 导出必须在 Python 里做,别试图用 C++ 替代
TensorRT 本身不提供模型训练或图结构生成能力。所谓“导出”,本质是把 PyTorch/TensorFlow 的计算图序列化为标准中间表示(ONNX),这一步依赖框架自身的导出逻辑和算子注册机制。
-
torch.onnx.export会调用 PyTorch 内部的 ONNX exporter,处理 control flow、自定义 op、动态 shape 等细节,C++ 没有对应实现 - 常见翻车点:用 C++ 调用 libtorch 的
torch::jit::save保存 TorchScript,再试图喂给 TensorRT —— 不行,TensorRT 8.x+ 已基本弃用对 TorchScript 的原生支持 - opset 版本必须对齐:YOLOv8 推荐
opset_version=17,ResNet 类模型常用opset_version=11,版本错位会导致parseFromFile静默失败或解析出错
trtexec 是首选的 ONNX → engine 转换工具,不是可选步骤
虽然你能手写 C++ 代码调用 IBuilder 构建引擎,但绝大多数实际项目应该先用 trtexec 验证流程是否通——它能暴露 90% 的模型兼容性问题,比如 unsupported op、shape inference 失败、dynamic axis 声明遗漏等。
- 基础命令:
trtexec --onnx=model.onnx --saveEngine=model.engine --fp16 --workspace=2048 - 动态输入必加
--optShapes=input:1x3x640x640,否则即使 ONNX 里标了 dynamic_axes,TensorRT 也会按静态 shape 处理 - 遇到
Assertion failed: tensors.count(output_name)这类错误,大概率是 ONNX 输出节点名和代码里getBindingIndex查的名字不一致,用onnxsim简化模型或netron打开检查输出名
C++ 加载 engine 文件比从 ONNX 构建更稳定、更快
生产环境应跳过每次运行时都 parse ONNX 的流程。序列化后的 .engine 文件已固化优化策略、kernel 选择、内存布局,加载即用,且规避了 parser 层的潜在 bug(尤其在 TensorRT 8.6+ 中,ONNX parser 对某些 op 组合仍不稳定)。
- 序列化:
engine->serialize()得到IHostMemory*,写入文件即可 - 反序列化:用
IRuntime::deserializeCudaEngine,注意传入的void*必须是完整 buffer,不能只传指针头 - 常见坑:
deserializeCudaEngine返回nullptr却不报错?检查 CUDA context 是否已创建、runtime 创建时传的 logger 是否有效、engine 文件是否被截断(用ls -l看大小,正常至少几百 KB)
binding index 和 tensor name 必须严格匹配,别信默认顺序
TensorRT 不保证输入/输出 binding 的顺序和 ONNX 中声明的顺序一致,尤其当模型有多个输入(如 YOLOv8 的 image + origin_shape)或非标准输出名时,硬编码 bindingIndex = 0 必崩。
- 务必用
engine->getBindingName(i)遍历所有 binding,用std::string匹配目标名,再调用engine->getBindingIndex(name.c_str()) - YOLOv8 导出的 ONNX 通常有
images输入和output0输出,但某些导出脚本会改成input/output,甚至带 batch 维度名(如input:0) - 调试技巧:在
createExecutionContext后,用context->getBindingIndex("xxx")打印结果,确认值 ≥ 0
最易被忽略的一点:TensorRT 引擎绑定的是 GPU 内存地址,所有 cudaMalloc 分配的 input/output buffer 必须用 cudaMemcpyAsync + 对应 stream 传输,且 lifetime 必须覆盖整个推理周期——buffer 提前 cudaFree 或 stream 同步时机错乱,会导致结果随机错乱,而非 crash。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











