共计 3016 个字符,预计需要花费 8 分钟才能阅读完成。
背景痛点
在广告推荐系统中,推理延迟直接影响用户体验和业务收益。传统 PyTorch 原生推理存在两个主要瓶颈:

- 响应时间:原始模型计算复杂度高,单次推理耗时可能超过 100ms,无法满足实时性要求
- 资源消耗:高并发场景下显存占用飙升,单台服务器 QPS 难以突破 2000
典型线上问题表现为:
- 高峰期广告召回超时率>5%
- GPU 利用率波动剧烈(30%~90%)
- 批处理效率低下导致长尾延迟显著
技术对比
主流推理加速框架特性对比:
| 特性 | TensorRT | ONNX Runtime | TorchScript |
|---|---|---|---|
| 算子覆盖率 | 85%+ | 90%+ | 95%+ |
| FP16 加速比 | 3-5x | 2-3x | 1.5-2x |
| 内存占用优化 | ★★★★★ | ★★★★☆ | ★★★☆☆ |
| 动态 Shape 支持 | 8.0+ 版本 | 原生支持 | 原生支持 |
| 部署复杂度 | 高 | 中 | 低 |
核心实现
PyTorch 转 ONNX 要点
关键转换代码示例(带异常处理):
import torch
from typing import Dict, Tuple
def export_onnx(model: torch.nn.Module,
sample_input: Dict[str, torch.Tensor],
output_path: str) -> None:
"""
Args:
model: 待转换的 PyTorch 模型
sample_input: 示例输入字典
output_path: ONNX 输出路径
"""
try:
dynamic_axes = {'input_ids': {0: 'batch_size'}, # 动态 batch 维度
'attention_mask': {0: 'batch_size'}
}
torch.onnx.export(
model,
args=tuple(sample_input.values()),
f=output_path,
input_names=list(sample_input.keys()),
output_names=['logits'],
dynamic_axes=dynamic_axes,
opset_version=13,
do_constant_folding=True
)
except RuntimeError as e:
print(f"导出失败: {str(e)}")
# 处理自定义算子问题
if "Unsupported operator" in str(e):
register_custom_op()
常见问题处理:
- 动态轴设置:必须明确标注可变维度(如 batch_size)
- 自定义算子:需通过
torch.autograd.Function注册 - 形状推断:使用
torch.onnx.export(operator_export_type=torch.onnx.OperatorExportTypes.ONNX_ATEN_FALLBACK)
FP16 量化实战
TensorRT 量化校准代码:
from tensorrt import Builder, Logger
class Calibrator(trt.IInt8EntropyCalibrator2):
def __init__(self, data_loader):
super().__init__()
self.data_loader = iter(data_loader)
self.cache_file = "calib.cache"
def get_batch_size(self) -> int:
return next(self.data_loader)[0].shape[0]
def get_batch(self, names):
try:
batch = next(self.data_loader)
return [int(batch[0].data_ptr())]
except StopIteration:
return None
# 构建量化引擎
def build_engine(onnx_path: str):
logger = Logger(Logger.INFO)
builder = Builder(logger)
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
parser = trt.OnnxParser(network, logger)
with open(onnx_path, "rb") as f:
if not parser.parse(f.read()):
for error in range(parser.num_errors):
print(parser.get_error(error))
config = builder.create_builder_config()
config.set_flag(trt.BuilderFlag.FP16)
config.set_flag(trt.BuilderFlag.INT8)
config.int8_calibrator = Calibrator(val_loader)
return builder.build_engine(network, config)
生产考量
内存管理优化
推荐方案:
- 预分配内存池:
cudaMalloc(&ptr, max_batch_size * max_seq_len * sizeof(float)) - 使用 CUDA Stream:
stream = torch.cuda.Stream() with torch.cuda.stream(stream): output = model(input)
批处理性能调优
实测数据(T4 GPU):
| Batch Size | 吞吐(QPS) | P99 延迟(ms) | GPU 显存(GB) |
|---|---|---|---|
| 1 | 1250 | 12.3 | 2.1 |
| 8 | 6800 | 18.7 | 3.8 |
| 32 | 9200 | 56.2 | 6.4 |
建议选择 batch_size= 8 作为平衡点
避坑指南
动态 Shape 陷阱
当输入尺寸变化时,TensorRT 会重建引擎。解决方案:
-
预生成常见尺寸的 engine:
for seq_len in [64, 128, 256]: profile = builder.create_optimization_profile() profile.set_shape("input", (1,seq_len), (8,seq_len), (32,seq_len)) config.add_optimization_profile(profile) -
使用
TrtGraphExecutor缓存引擎
多线程竞争
典型错误现象:CUDA_ERROR_ILLEGAL_ADDRESS。正确做法:
- 每个线程独立 CUDA context
- 或使用
torch.inference_mode()全局锁
延伸思考
结合模型蒸馏的复合加速方案:
-
教师 - 学生架构:
distiller = Distiller( teacher_model=original_model, student_model=tiny_model, temperature=2.0 ) distiller.train(soft_labels=True) -
量化感知训练:
model = quantize_model( model, quant_config=QConfig(activation=MinMaxObserver.with_args(dtype=torch.qint8), weight=MinMaxObserver.with_args(dtype=torch.qint8) ) )
实测组合方案可进一步获得 2 - 3 倍加速比,模型体积缩小 60%。
正文完
