行业资讯
📅 2026/8/15 8:33:33
【TensorRTtSharp v4.0】使用 TensorRT CSharp API 完成 Dynamic Shape 与动态 Batch 推理
生产环境中的输入尺寸很少永远固定服务端会合并不同数量的请求图像模型可能接收多种分辨率序列模型的长度也会变化。TensorRT 用动态维度和 Optimization Profile 描述这些变化范围但仅把输入维度写成-1并不足以完成推理。构建阶段、运行阶段和显存绑定阶段必须使用一致的 shape。本文从仓库自带的DynamicShape示例出发构建一个动态 batch 的 Identity 网络并在真实 NVIDIA GPU 上完成序列化、反序列化、输入绑定、异步执行和输出校验。示例刻意保持网络很小方便把注意力放在 Dynamic Shape 的完整调用顺序上。本文是 TensorRT CSharp API v4.0 4.0.0 Samples 系列的SMP-003对应源码samples/Inference/02.DynamicShapes。文中的 API、包版本、命令和结果均以4.0.0为基线。1. 前言TensorRT CSharp API v4.0 是一个面向 C#/.NET 开发者的 TensorRT 与 CUDA 工程化接口项目。它把 NVIDIA 原生运行时、生成式绑定、C Bridge、托管对象模型和可验证的示例程序组织成一条完整链路使使用者可以在熟悉的 .NET 项目中完成 Engine 构建、反序列化、ExecutionContext 管理、CUDA 内存操作、异步流同步和结果校验。项目的目标不是隐藏 TensorRT 的概念而是把这些概念转换为有明确生命周期、所有权和错误边界的 C# API。4.0.0 是一次完整重构后的正式版本。核心接口、Bridge 边界、Runtime 包命名、样例目录和验证方式都以 4.x 设计为准不能把 3.x 的类型名、旧包名或旧 DLL 目录直接复制到新项目。托管包只提供项目接口和自有 BridgeTensorRT、CUDA、cuDNN、显卡驱动以及对应许可证仍由使用者按目标平台安装和管理。单篇文章也应能够独立阅读读者可以先从项目入口确认源码和包再根据本文的程序路径准备依赖最后用输出中的状态、计数、Shape、哈希或结果图片判断流程是否真的完成。对于尚未具备兼容 GPU 的环境本文会把静态检查、期望输出和真实运行结果分开标记不把帮助命令或 build-only 结果包装成推理成功。项目、包和源码入口以下地址保留明文便于复制到不完整支持 Markdown 链接的平台项目主页https://github.com/guojin-yan/TensorRT-CSharp-API/tree/TensorRtSharp4.0核心 NuGethttps://www.nuget.org/packages/JYPPX.TensorRT.CSharp.API/4.0.0Runtime Bridge 包列表https://www.nuget.org/packages?qJYPPX.TensorRT.CSharpincludeComputedFrameworkstrueprereltruesortbyrelevance运行库清单https://github.com/guojin-yan/TensorRT-CSharp-API/blob/TensorRtSharp4.0/pack/runtime/runtime-packages.manifest.json1.1 程序出处与输出说明本文涉及的程序、脚本或命令均以仓库中的实现为准对应源码入口https://github.com/guojin-yan/TensorRT-CSharp-API/tree/TensorRtSharp4.0/samples运行示例必须同时给出程序输出和判定标准。终端中的status、Ready、Bound、enqueueCount、OutputMatch、进程退出码、报告文件或结果图片分别说明不同层次的事实只有明确写出这些结果读者才能区分程序启动、Engine 构建、GPU enqueue 和业务结果语义。1.2 项目简介TensorRT CSharp API v4.0 为 .NET 开发者提供 TensorRT Builder、Optimization Profile、ExecutionContext、Bindings 和 CUDA Stream 的托管封装。Dynamic Shape 是最能体现这套 API 价值的基础能力之一输入尺寸变化时应用仍可以用明确的 profile 和 readiness 检查控制执行边界。1.3 项目链接与包列表项目内容入口项目源码TensorRT-CSharp-API 4.0 分支https://github.com/guojin-yan/TensorRT-CSharp-API/tree/TensorRtSharp4.0本文案例源码samples/Inference/02.DynamicShapeshttps://github.com/guojin-yan/TensorRT-CSharp-API/tree/TensorRtSharp4.0/samples/Inference/02.DynamicShapes案例入口代码Program.cshttps://github.com/guojin-yan/TensorRT-CSharp-API/blob/TensorRtSharp4.0/samples/Inference/02.DynamicShapes/Program.cs核心 NuGetJYPPX.TensorRT.CSharp.API 4.0.0https://www.nuget.org/packages/JYPPX.TensorRT.CSharp.API/4.0.0Runtime BridgeNuGet 包列表https://www.nuget.org/packages?qJYPPX.TensorRT.CSharpincludeComputedFrameworkstrueprereltruesortbyrelevance案例本身使用内存 Identity 网络不需要外部模型或 ONNX真实 GPU 运行需要与目标 TensorRT/CUDA/cuDNN 组合匹配的*.Bridge 4.0.0。1.4 本文结构先介绍动态输入和 profile 的约束再逐步实现网络、构建 Engine、设置运行时 shape、绑定地址、执行和校验最后总结动态 shape 常见失败原因。2. 适用读者本文适合已经能够编译 .NET 项目希望了解 TensorRT 动态输入、Optimization Profile 和执行前诊断的开发者。阅读后可以把相同流程迁移到分类、检测、分割或序列模型。3. 本文解决什么问题本文集中解决三个经常混在一起的问题如何声明动态维度并为它配置合法的 min/opt/max 范围。如何在每次推理前设置真实输入 shape、分配显存并绑定 tensor address。如何在 enqueue 前判断 profile、shape 和地址是否已经全部就绪。4. 本文使用的项目与库组件本文中的职责TensorRT CSharp API v4.0提供 TensorRT 和 CUDA 的 owner-safe C# 接口。JYPPX.TensorRtSharp创建 builder、network、profile、engine 和 execution context。JYPPX.CudaSharp创建非阻塞 CUDA stream并测量 GPU 执行耗时。JYPPX.SampleSupport解析命令行参数并选择 TensorRT 适配器。NVIDIA TensorRT构建并运行动态 shape 的 Identity 网络。.NET编译和运行 C# 示例。示例项目通过仓库统一配置消费稳定版JYPPX.TensorRT.CSharp.API4.0.0自身不参与打包。运行它仍然需要与目标 TensorRT/CUDA ABI 对应的 Bridge以及用户安装的 NVIDIA 运行库。本文实测环境为 Windows 11、NVIDIA GeForce RTX 3060 Laptop GPU、驱动 576.02、CUDA 12.9、TensorRT 10.11.0.33 和 .NET SDK 10.0.301。TensorRT、CUDA 与显卡驱动由用户自行安装仓库及其发布物不包含 NVIDIA 运行库。5. 模型获取与 ONNX 转换这个示例没有使用深度学习模型也不需要 ONNX 文件。网络由 C# 在内存中创建输入经过一个 Identity layer 后原样输出。因此不存在权重下载、模型许可证、ONNX 转换或外层models暂存文件。这种设计的目的不是展示识别效果而是隔离并验证 Dynamic Shape 的基础调用路径。接入真实 ONNX 模型时模型的输入名、动态维度和 Optimization Profile 范围仍需按本文相同顺序配置。6. 环境准备先安装与目标版本匹配的 NVIDIA 驱动、CUDA Toolkit 和 TensorRT并确认仓库已经生成对应版本的jyppxtrtbridge.dll。进入仓库根目录后用变量保存本机 TensorRT 安装目录$RepoRoot(Get-Location).Path$env:TENSORRT_PATH 你的 TensorRT 安装目录$env:JYPPX_TENSORRT_ROOT $env:TENSORRT_PATH$env:JYPPX_NATIVE_BRIDGE_PATH Join-Path$RepoRootbuild-out\win-x64-trt10-cuda12-release\bin\Release\jyppxtrtbridge.dll$env:JYPPX_ENABLE_DEVELOPMENT_PROBING true桥接库只封装 TensorRT/CUDA ABITensorRT、CUDA、cuDNN 和 NVRTC 始终从用户机器的安装目录加载。7. 理解示例网络示例源文件位于samples/Inference/02.DynamicShapes/Program.cs网络结构如下input [-1, 3, 4] - Identity - output [-1, 3, 4]第一维的-1表示 batch 在构建时未知。示例为它创建一个 Optimization ProfileProfile 位置Shape含义min[1, 3, 4]允许的最小 batch。opt[2, 3, 4]builder 优先优化的典型 batch。max[4, 3, 4]允许的最大 batch。本文运行时选择 batch 3因此实际输入 shape 为[3, 3, 4]共有 36 个float。声明动态输入 [-1,3,4]配置 Profile min/opt/max构建并反序列化 Engine设置运行时 Shape [3,3,4]复制输入并绑定地址检查 ReadinessEnqueueAsync读回并逐值比较8. 核心代码8.1 声明动态输入usingTensorRtNetworkDefinitionnetworkbuilder.CreateNetwork(TensorRtNetworkDefinitionCreationFlags.ExplicitBatch);usingTensorRtTensorinputTensornetwork.AddInput(input,TensorRtDataType.Float,newTensorRtDims(new[]{-1,3,4}));usingTensorRtLayeridentitynetwork.AddIdentity(inputTensor);usingTensorRtTensoroutputTensoridentity.GetOutput(0);outputTensor.Nameoutput;network.MarkOutput(outputTensor);8.2 添加 Optimization ProfileusingTensorRtOptimizationProfileprofilebuilder.CreateOptimizationProfile();profile.SetShape(input,newTensorRtDims(new[]{1,3,4}),newTensorRtDims(new[]{2,3,4}),newTensorRtDims(new[]{4,3,4}));TensorRtOptimizationProfileShapeRangeprofileRangeprofile.GetShapeRange(input);boolprofileValidprofile.IsValid;intprofileIndexconfig.AddOptimizationProfile(profile);min、opt和max的 rank 必须一致而且每一维都要满足min opt max。profile.IsValid是构建前的第一道检查。8.3 设置运行时 shape 并绑定显存TensorRtDimsruntimeShapenew(new[]{batch,3,4});usingTensorRtInferenceBindingsbindingsnewTensorRtInferenceBindings(engine,context,profileIndex);bindings.SetInputShape(input,runtimeShape).CopyInputFromHost(input,inputValues,runtimeShape);bindings.AllocateDeviceBuffer(output,runtimeShape,checked(inputValues.Length*sizeof(float)));bindings.BindAll();8.4 在执行前检查状态TensorRtExecutionContextReadinessreadinessbindings.GetReadiness(runShapeInference:true);if(!readiness.IsReadyForEnqueue){thrownewInvalidOperationException(Dynamic shape bindings are not ready: readiness);}GetReadiness会集中报告输入 shape、active profile 和 tensor address 状态。这样可以在真正 enqueue 前发现配置错误而不是等 TensorRT 返回一条难定位的底层错误。8.5 执行并验证输出TensorRtInferenceExecutionSummaryexecutionSummarynull!;floatelapsedMillisecondsstream.MeasureElapsedTime(cudaStream{executionSummarybindings.EnqueueAsync(cudaStream,synchronize:false,runShapeInference:false);});float[]outputValuesbindings.ReadOutputSingles(output,inputValues.Length);booloutputMatchinputValues.SequenceEqual(outputValues);Identity 网络的输出应该与输入逐值相等所以OutputMatchTrue是比“进程没有崩溃”更明确的正确性检查。9. 编译与运行从仓库根目录执行 Release 编译dotnet build.\samples\Inference\02.DynamicShapes\DynamicShape.csproj -c Release --no-restore /p:UseSharedCompilationfalse运行 batch 3dotnet.\samples\Inference\02.DynamicShapes\bin\Release\net8.0\DynamicShape.dll --tensor-rt-line 10 --batch 3参数说明参数含义–tensor-rt-line 810--batch 1..4选择 profile 范围内的运行时 batch本文使用 3。10. 真实运行结果下面是上述命令在实测环境中的完整 Windows Terminal 运行窗口。截图来自同一次真实运行的 stdout不是重新排版的指标卡片。终端中的关键输出如下DynamicShape TensorRtLine10 TRT10.11.0 CUDA12.9 Batch3 Network Inputinput:[-1, 3, 4] Outputoutput:[-1, 3, 4] Profile Index0 Min[1, 3, 4] Opt[2, 3, 4] Max[4, 3, 4] ValidTrue RuntimeShape[3, 3, 4] Values36 HostMemory3332 EngineTensors2 Readiness ReadyTrue BoundTrue ActiveProfile0 BindingReport ReadyTrue Inputs1 Outputs1 Execution profile0 bound2 synchronizedFalse readyTrue ElapsedMs0.629 OutputMatchTrue DynamicShape PassedTrue ProcessExitCode0检查项实测结果说明ProfileValidTruemin/opt/max 被 TensorRT 接受。Runtime shape[3, 3, 4]batch 3 位于允许范围内。Tensor addressBoundTrue输入与输出地址均已绑定。Binding report1 input / 1 outputEngine I/O 与预期一致。输出校验OutputMatchTrue36 个输出值与输入逐值一致。GPU 计时0.629 ms本次运行记录不作为跨机器性能基准。进程状态ProcessExitCode0示例正常结束。本次运行的机器可读记录位于samples/assets/dynamic-shape-article-runtime-evidence.json。记录中保存了源文件、程序集、桥接库、原始日志和截图的 SHA256便于确认正文、截图与运行产物是否对应。2026-08-04 完成共享命名空间迁移后再次执行同一 Release 示例结果为ProcessExitCode0、OutputMatchTrue耗时0.724 ms。maintenanceValidation记录当前源码、程序集和运行日志 SHA256正文继续使用 2026-08-03 的真实终端截图并以runtimeScreenshotRecapturedfalse明确它不是本次重拍图片。11. 常见问题11.1 Batch 超出 Profile 范围执行--batch 8会在进入 TensorRT 前失败因为示例只允许[1, 4]。真实模型也应先在应用层校验输入再调用SetInputShape。11.2 输出DynamicShapeSkipped这表示当前机器无法创建 TensorRT runtime 或 builder常见原因是 TensorRT/CUDA 安装目录不完整、桥接库版本不匹配或开发探测没有启用。它是环境诊断结果不代表推理成功。11.3ReadyFalse或BoundFalse先检查是否对每个动态输入调用了SetInputShape再检查所有输入输出是否已经分配并绑定 device buffer。多输入模型必须为每个动态输入分别设置 shape。11.4 CUDA error 35该错误通常表示当前显卡驱动无法支持所加载的 CUDA runtime。应调整驱动、CUDA、TensorRT 和桥接库的版本组合而不是修改 Dynamic Shape 逻辑绕过错误。12. 读取 Engine 中的 Profile Tensor Values当网络包含 shape tensor input 时可以从构建后的 engine 读取某个 profile 的 min/opt/max 值。TensorRT CSharp API v4.0 会复制 TensorRT 返回的数据不把 borrowed pointer 暴露给调用者long[]optValuesengine.GetProfileTensorValuesV2(shape_input,profileIndex:0,TensorRtOptimizationProfileSelector.Opt);如果 tensor shape 仍含未知维度自动推导无法确定复制数量应改用带valueCount的重载long[]optValuesengine.GetProfileTensorValuesV2(shape_input,profileIndex:0,TensorRtOptimizationProfileSelector.Opt,valueCount:expectedShapeValueCount);13. 本文结论与边界这次实测证明了源码树中的 TensorRT 10 Dynamic Shape、Optimization Profile、binding readiness、CUDA stream enqueue 和 GPU readback 路径能够协同工作并且 batch 3 的 36 个输出值全部匹配。它不证明任意 ONNX 模型的布局、精度或后处理正确也不替代真实模型验证。本文没有执行 NuGet 发布、GitHub Packages 发布、Release 创建或 post-publish 验证截图与证据只对应本次本机源码运行。14. 文章声明14.1 开源协议声明作者所有开源项目代码均遵循 Apache License 2.0 开源协议。特别说明本项目集成了若干第三方库。若任何第三方库的许可协议与 Apache 2.0 协议存在冲突或不一致均以该第三方库的原始许可协议为准。本项目不包含也不代表这些第三方库的授权声明使用前请务必阅读并遵守第三方库的相关许可。14.2 代码开发与质量说明AI 辅助开发本代码在开发过程中使用了人工智能AI辅助生成与优化并非完全由人工逐行编写。安全性承诺作者郑重声明本代码中绝无任何有意设置的后门、病毒、木马或旨在破坏用户设备、窃取数据的恶意代码。技术局限性受限于作者个人的技术水平与能力代码中可能存在因逻辑不严谨、优化不足或经验欠缺导致的低级问题例如但不限于内存泄漏、偶发崩溃、资源未释放等。这些问题纯属能力不足所致并非主观故意。测试范围由于作者精力有限未对本软件进行全方位、覆盖所有边缘场景的完整测试。14.3 免责声明重要请在将本代码应用于任何实际项目特别是商业、工业或关键任务环境之前务必进行详尽、严格的自行测试与验证。 鉴于上述可能存在的代码缺陷及测试覆盖不足因使用本代码而导致的任何直接或间接损失包括但不限于设备故障、数据丢失、系统瘫痪或利润损失等本作者概不负责。 一旦您开始使用本代码即表示您已知晓上述风险并同意自行承担一切后果相关问题与本作者无关。14.4 代码开源范围本项目承诺核心逻辑代码完全开源但上述提到的“第三方库”的二进制文件、源代码或相关资源不在本项目的开源义务范围内请根据其各自的指引获取。14.5 社区与反馈尽管存在上述不足我们仍欢迎大家下载使用、提交 Issue 或参与测试共同完善项目。如果您在使用过程中发现 Bug、内存溢出或有改进建议欢迎通过项目主页提供的联系方式与作者取得联系我们将尽力在有限的时间内提供协助。