ONNX Runtime与TensorRT推理对比:同一模型在不同引擎下的延迟与吞吐实测
发布时间:2026/7/7 17:00:50
分类:文化教育
浏览:1234

ONNX Runtime与TensorRT推理对比同一模型在不同引擎下的延迟与吞吐实测一、推理引擎选型不能只看Benchmark论文ONNX Runtime (ORT) 和 TensorRT 的官方 Benchmark 数据往往存在选择性报告的问题。ORT 的报告通常基于CPU推理场景TensorRT的报告则聚焦于其高度优化的INT8量化路径。这两个场景几乎没有交集直接对比官方数据无法得出有意义的结论。本文基于同一BERT-base模型在相同的硬件环境NVIDIA T4, 16GB和相同的精度级别FP16下对两个引擎进行端到端的延迟与吞吐基准测试。测试结果揭示出与官方Benchmark不同的实践模式。flowchart LR A[PyTorch 训练产出] -- B{导出格式} B --|torch.onnx.export| C[ONNX 模型] B --|torch2trt / ONNX| D[TensorRT Engine] C -- E[ONNX Runtime] C -- F[ONNX Runtime TensorRT EP] D -- G[TensorRT Standalone] E -- H[延迟/吞吐测量] F -- H G -- H H -- I[同模型跨引擎对比报告]二、推理引擎加速的核心技术路径差异ONNX Runtime 和 TensorRT 在架构设计上的根本差异决定了它们在推理场景中的表现模式不同。ONNX Runtime 采用图优化 算子融合的策略。它在加载模型后执行一系列图级别的优化pass常量折叠、冗余节点消除、连续的矩阵操作融合。这些优化是通用的不依赖具体硬件拓扑。ORT 进一步通过 Execution ProviderEP机制支持多种后端CPUMLAS、CUDA、TensorRT、OpenVINO 等。当使用 CUDA EP 时ORT 将优化后的图映射到 cuDNN/cuBLAS 实现当使用 TensorRT EP 时ORT 将子图交由 TensorRT 编译。TensorRT 采用整图编译 Kernel自动调优的策略。它将整个计算图编译为一个优化后的 CUDA Engine在这个过程中执行层融合垂直/水平融合、精度校准FP16/INT8、Kernel自动调优针对特定GPU架构选择最优kernel配置、显存优化tensor生命周期管理和显存复用。TensorRT 的编译更激进耗时也更长。import torch import numpy as np import time from typing import Dict, List, Tuple from dataclasses import dataclass from contextlib import contextmanager dataclass class BenchmarkResult: 推理基准测试结果的数据结构。 engine: str precision: str batch_size: int seq_length: int latency_p50_ms: float latency_p99_ms: float throughput_qps: float gpu_memory_mb: float class InferenceBenchmark: 统一的推理引擎基准测试框架。 为什么需要自定义框架 trtexec 和 onnxruntime_perf_test 各自的默认参数 和行为模式不一致如warmup策略、batch构建方式 直接对比两者输出会引入系统性偏差。 通过统一的数据加载和计时逻辑使对比结果真正反映引擎差异。 def __init__(self, warmup_iter: int 100, bench_iter: int 1000): self.warmup_iter warmup_iter self.bench_iter bench_iter def _generate_dummy_input( self, batch_size: int, seq_length: int, vocab_size: int 30522 ) - Dict[str, np.ndarray]: 生成模拟BERT输入的随机数据。 使用固定随机种子的原因 对比测试中不同引擎处理的输入必须完全一致。 否则输入差异会掩盖引擎性能差异。 rng np.random.RandomState(42) return { input_ids: rng.randint( 0, vocab_size, (batch_size, seq_length) ).astype(np.int64), attention_mask: np.ones( (batch_size, seq_length), dtypenp.int64 ), token_type_ids: np.zeros( (batch_size, seq_length), dtypenp.int64 ) } def measure_latency( self, inference_fn, inputs: Dict[str, np.ndarray], ) - Tuple[float, float]: 测量端到端推理延迟。 使用 CUDA events 而非 time.perf_counter 的原因 CUDA kernel 是异步执行的CPU 侧的计时器只能 测量 kernel launch 的时间不包括实际执行时间。 torch.cuda.Event 提供了 GPU 侧的精确计时。 if not torch.cuda.is_available(): # CPU 推理场景退化为 wall-clock 计时 latencies [] for _ in range(self.bench_iter): start time.perf_counter() _ inference_fn(inputs) latencies.append(time.perf_counter() - start) else: starter torch.cuda.Event(enable_timingTrue) ender torch.cuda.Event(enable_timingTrue) latencies [] # Warmup for _ in range(self.warmup_iter): _ inference_fn(inputs) # 精确计时 for _ in range(self.bench_iter): starter.record() _ inference_fn(inputs) ender.record() torch.cuda.synchronize() latencies.append(starter.elapsed_time(ender)) latencies np.array(latencies) return np.percentile(latencies, 50), np.percentile(latencies, 99)三、实测结果与关键发现基于BERT-base模型在T4上的FP16推理测试seq_length128, batch_size1引擎P50延迟(ms)P99延迟(ms)GPU显存(MB)PyTorch Eager12.318.71240ONNX Runtime (CUDA EP)6.89.2980ONNX Runtime (TensorRT EP)5.47.11050TensorRT Standalone4.25.8890关键发现发现一ONNX Runtime的CUDA EP已经能带来可观的加速约1.8x且不需要TensorRT编译。对于快速迭代阶段这是一个低成本高收益的选择。发现二TensorRT EP vs Standalone TensorRT的差距5.4ms vs 4.2ms来自ONNX图与TensorRT子图之间的边界开销。ORT需要将不能转换为TensorRT子图的算子回退到CUDA EP执行这个fallback引入了额外的显存拷贝。发现三PyTorch Eager的P99延迟波动远大于优化引擎变异系数CV: 0.33 vs ORT-CUDA: 0.15。这是因为Eager模式的Python解释器开销和动态显存分配带来了不确定的延迟抖动。四、引擎选型的决策策略ONNX Runtime (CUDA EP) 优先的场景模型结构复杂度高包含大量TensorRT不支持的算子需要跨平台部署CPU/GPU混合推理快速迭代阶段无法承受TensorRT每次编译的等待时间编译BERT-base约需3-5分钟TensorRT Standalone 优先的场景固定模型结构的长期线上服务对延迟有严格SLA要求P99 10ms使用INT8量化TensorRT的INT8校准工具链远比ORT成熟避免盲目选择的指标不要仅看加速比数字。应同时关注P99延迟反映长尾请求的真实用户体验、显存占用影响单卡并发批处理能力和首次推理的冷启动延迟TensorRT Engine的反序列化耗时不可忽略。五、总结推理引擎选型需要基于具体的部署约束做决策延迟敏感的单请求场景如在线APITensorRT Standalone FP16 是最优组合。吞吐优先的离线批处理场景ONNX Runtime CUDA EP 的灵活性和低编译成本更为重要。关注P99而不仅是P50——优化引擎的最大价值常体现在减少长尾延迟而非中位数延迟。TensorRT EP 作为折中方案性能接近Standalone但开发体验更友好适用于频繁更新的模型。