3. OptimizationProfile参数详解

在 TensorRT 的动态 Shape 部署中,OptimizationProfile(优化配置文件)是连接“网络定义”与“运行时实际输入”的桥梁。它告诉构建器:这个网络在运行时可能遇到哪些输入尺寸范围,以及在哪个尺寸上最值得做极致优化。一个 Profile 由三组维度构成:min、opt、max,它们共同描述了一个输入张量的合法取值空间。

min / opt / max 的语义

对于每个动态输入张量,Profile 要求给出三个维度向量:

  • min shape:运行时允许的最小尺寸,任何小于它的输入都会导致 setInputShape 失败或推理结果不可信。
  • max shape:运行时允许的最大尺寸,超过它同样会被拒绝。
  • opt shape:构建器认为“最典型、最常出现”的尺寸。TensorRT 会以 opt shape 为基准进行 kernel 自动调优(tactic selection),因此 opt 选得准不准,直接决定实际推理性能。

三者必须满足逐维约束:min[i] ≤ opt[i] ≤ max[i]。注意这是逐维关系,而不是整体体积关系。例如 min=[1,3,224,224]、opt=[4,3,512,512]、max=[8,3,608,608] 是合法的;但如果某一维出现 min[i] > opt[i],构建阶段就会报错。

opt shape 不要求等于 min 或 max,也不要求是它们的算术平均。工程上通常取“业务中最常见的 batch 与分辨率组合”,例如在线服务取 batch=1、离线批处理取 batch=8。

多输入多输出 Profile 的维度约束

当网络有多个动态输入时,一个 Profile 必须为每一个动态输入都指定 min/opt/max。TensorRT 在运行时按 Profile 整体匹配:调用 setInputShape(tensorName, shape) 时,所有输入的实际 shape 必须同时落在同一个 Profile 的范围内,不能出现“输入 A 用 Profile 0、输入 B 用 Profile 1”的混用。

输出张量的维度通常由输入和网络结构推导得出,不需要在 Profile 中显式指定;但若输出维度依赖 shape tensor 或数据相关操作,则其范围会随输入范围自动变化,需要保证 max 输入下输出不会超出显存预算。

多 Profile 的典型用途是覆盖差异较大的业务场景。例如同一检测网络:

Profile输入 NCHW (min)输入 NCHW (opt)输入 NCHW (max)适用场景
P0[1,3,320,320][1,3,416,416][1,3,640,640]实时单帧
P1[4,3,320,320][8,3,416,416][16,3,640,640]离线批处理

运行时通过 context.setOptimizationProfileAsync(profileIndex, stream) 切换 Profile。切换后必须重新设置所有输入 shape,否则会沿用旧值导致维度不匹配。

shape tensor 与 execution tensor 的区别

这是动态 Shape 中最容易混淆的一对概念,必须从“张量在图中扮演什么角色”来区分。

  • Execution tensor(执行张量):承载真实数据的张量,参与卷积、矩阵乘等计算,其 shape 在运行时由 Profile 约束。例如输入图像张量 input:0、输出检测框张量 output:0。
  • Shape tensor(形状张量):承载“维度数值”的整型张量,本身不参与数值计算,而是被其他算子用来决定输出形状。例如 shape_of(input) 的输出、gather 取出的某一维长度、concat 拼接出的目标尺寸。

关键差异在于:execution tensor 的 shape 由 Profile 的 min/opt/max 直接约束;而 shape tensor 的值在运行时才确定,构建器只能根据 Profile 范围推断其可能取值。因此,凡是依赖 shape tensor 的算子(如 resize、slice、fill),其输出维度在构建期是“范围”而非固定值。

OptimizationProfile 与两类张量的关系 OptimizationProfile min = [1,3,320,320] opt = [1,3,416,416] max = [1,3,640,640] 约束 execution tensor 的 shape Execution Tensor input:0 [N,3,H,W] 承载真实像素数据 Shape Tensor shape_of(input) → [4] 承载维度数值,不参与计算 约束 推断范围 数值算子 conv / gemm / pool 输出 shape 由输入决定 形状算子 resize / slice / fill 输出 shape 由 shape tensor 决定 运行时:context.setInputShape("input:0", [1,3,416,416]) → 必须落在同一 Profile 的 min/max 之间 shape tensor 的值随实际输入变化,构建器只能按 Profile 范围预留显存与选择 tactic

实际配置示例

下面用 Python API 展示一个双输入网络的 Profile 设置过程。假设网络有两个动态输入:image(NCHW)和 scale(标量 shape tensor)。

profile = builder.create_optimization_profile()

# execution tensor:图像输入
profile.set_shape(
    "image",
    min=(1, 3, 320, 320),
    opt=(1, 3, 416, 416),
    max=(1, 3, 640, 640)
)

# shape tensor:标量,值域固定为 [1]
profile.set_shape("scale", min=(1,), opt=(1,), max=(1,))

config.add_optimization_profile(profile)

# 运行时
context.set_optimization_profile_async(0, stream)
context.set_input_shape("image", (1, 3, 416, 416))
context.set_input_shape("scale", (1,))

这里 scale 是 shape tensor,它的“shape”是 (1,),而它的“值”在运行时通过 set_input_shape 之外的方式(如 set_tensor_address 配合 host 数据)写入。很多初学者会把 shape tensor 的 shape 与 value 混淆,导致 set_input_shape 报维度错误。

常见错误:对 shape tensor 调用 set_input_shape 时传入了错误的维度数量。shape tensor 的 shape 是它自身的维度(如 (1,) 或 (4,)),而不是它承载的数值。例如 shape_of 输出是 4 维向量,其 shape 为 (4,),不是 (1,3,H,W)。

Profile 选择与性能判断

构建完成后,可以通过 engine.get_profile_shape(profile_index, tensor_name) 回读每个 Profile 的 min/opt/max,确认与预期一致。运行时若输入 shape 落在多个 Profile 的交集内,TensorRT 会选择第一个匹配的 Profile,因此 Profile 的注册顺序会影响实际使用的优化策略。

判断 Profile 是否合理,可以观察两个信号:一是 set_input_shape 是否成功返回;二是推理延迟是否接近 opt shape 下的预期。如果实际输入长期偏离 opt,性能会明显下降,此时应调整 opt 或增加 Profile 覆盖该尺寸段。

最后强调一点:min/opt/max 的设定不是“越宽越好”。范围越宽,构建器需要为更多尺寸组合准备 kernel,构建时间与显存占用都会上升。工程上应基于真实业务分布收窄范围,把 opt 放在最高频的尺寸上,这才是 Profile 调优的核心。