想象你经营着一家既能办百人婚宴、又能做街边小吃的复合餐厅。传统做法是为宴席准备固定尺寸的圆桌、为快餐准备固定的单人餐台——客人一多一少,桌椅就派不上用场,只能不停地重新布置。TensorRT 早先的“静态 Shape”模式正是这样:模型输入尺寸固定为 32、128、256,就得分别建三套完全独立的引擎,像三间不同规格的厨房一样各占显存、各出人力。而这个教程的主角——动态 Shape,则像把餐厅改造成了灵活的自助餐线:餐台宽度可以随客流伸缩,备料仓库统一调度,主厨根据当下客人数量动态挑选最合适的灶台与炒菜节奏。客人(输入数据)换了一拨又一拨,长度变了又变,厨房不需要重新装修,开张一次就能接待所有尺寸的客人。这个“自助餐厅”不仅省了空间,还保证了出餐速度,正是 NLP 等变长输入场景梦寐以求的推理能力。

岗位关联

对 AI 从业者而言,掌握动态 Shape 意味着你交付的推理服务不再是“只能吃固定套餐”的僵化模型,而是能从容应对真实世界长短不一的数据流——这是从“能跑通 demo”迈向“能上生产环境”的关键一步,也是面试部署工程师岗位时绕不开的核心考点。

目录
  1. 可视化演示
  2. 动态Shape概念
  3. 优化Profile
  4. 引擎构建流程
  5. 运行时绑定
  6. 内存池管理
  7. 性能优化策略
  8. 部署应用场景
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示TensorRT动态Shape机制:变长输入张量、优化Profile定义维度范围、运行时自动选择最优内核,通过交互理解动态Shape兼顾灵活性与推理性能的原理。

本节配有交互动效(进入视口后自动加载,约 3KB)。

动态Shape:变长输入处理 引擎处理变长输入无需逐尺寸构建 适合NLP等变长输入场景 Shape ∈ [Min, Max] 动态Shape兼顾灵活与性能

动态Shape概念

传统宴席上,每位客人的餐盘大小是统一的,厨房提前把菜分好,盘子端上去就是那个分量——这就是 TensorRT 的静态 Shape 模式。可自助餐厅的客人饭量不一:有人只想尝两口,有人要满满一盘。如果厨房还是按固定的盘子配菜,要么给每桌备上好几套规格的餐具(为每个尺寸单独建引擎),要么干脆把所有人的菜都堆成大份(padding 到最大长度)。前者管理成本爆炸,后者浪费大量算力。动态 Shape 让餐台本身能伸缩:输入数据的尺寸每次都可以不同,同一套厨房流程照常运转。

这个“伸缩的餐台”落到技术上,就是输入张量的 shape 中可以出现动态维度。在 TensorRT 里,我们通常用 -1 标记一个允许变化的维度。例如 NLP 模型处理句子序列时,批量大小固定为 1,隐藏层特征固定为 64,只有序列长度每次请求都不同,于是把输入定义为 (1, -1, 64)。

没有动态 Shape 时,线上句子长度在 8 到 256 之间浮动,你只能选两个方案:按最大长度 padding,GPU 上四分之三的算力在算“空菜”;或者为每个常见长度各建一个引擎,部署文件多到运维崩溃。动态 Shape 让一个引擎通吃整个区间,像一间自助厨房服务所有饭量的客人。

inputShape=(N,  −1,  D)\text{inputShape} = \left( N,\; -1,\; D \right)
这个公式是说:输入张量有三个维度,第一个 N 和第三个 D 是固定值,中间那个 -1 是一个“活口”,允许每次推理时填入不同的序列长度。
  1. N 固定批次大小批量大小在构建引擎时就确定,本例中固定为 1,代表一次只处理一条序列。
    N=1N = 1
  2. -1 动态维度占位负一不是真的负一,而是告诉 TensorRT:“这个维度别锁死,运行时我会告诉你具体数值”。
    −1  ⇒  待定动态维度-1 \;\Rightarrow\; \text{待定动态维度}
  3. D 固定特征维度每个 token 的向量长度是模型结构决定的,始终不变。
    D=64D = 64

优化Profile

自助餐要想开得顺畅,得先划好“取餐区间”:凉菜区备 3 到 8 种、热菜区备 5 到 12 种。客人少了就少上菜,客人多了就多补货,但总得有个下限和上限。TensorRT 的优化 Profile(Optimization Profile) 干的正是这件事——为每个动态维度声明一段合法区间,并且还要在这段区间里标出一个“黄金档位”。

Profile 用三个值描述一个动态维度:min 是下界,表示引擎接受的最小长度;max 是上界,表示显存和内核设计能支撑的最大长度;opt 是“最常出现的长度”,相当于主厨最拿手的分量。构建引擎时,TensorRT 会重点围绕 opt 调优内核;运行时,每次输入的实际长度只要落在 区间内就能正常运行。

划 Profile 有点像给自助餐线定“餐台容量区间”:下限定得太低、上限定得太高,主厨要兼顾的范围太广,每个尺寸都难做到极致;区间贴合真实客流量分布,才能既灵活又高效。这也是为什么部署前统计线上输入尺寸分布至关重要。

minDim  ≤  optDim  ≤  maxDim\text{minDim} \;\le\; \text{optDim} \;\le\; \text{maxDim}
动态维度不是无限自由的,它必须在 min 和 max 圈定的围栏里活动;而 opt 则是围栏里那条被精心打磨过的“最佳跑道”。
  1. minDim 下界保证最小输入也能正常推理,通常取线上可能出现的最短序列,比如一句话的十几个 token。
    minDim=32\text{minDim} = 32
  2. optDim 最优档位这个值应该取实际部署中最常见的形状,TensorRT 会重点为这个尺寸选择最得意的内核。
    optDim=128\text{optDim} = 128
  3. maxDim 上界超过这个值的输入会直接报错,它同时也是构建时显存预留的天花板。
    maxDim=256\text{maxDim} = 256

引擎构建流程

自助餐厅开业前要做的不是每天临时摆桌子,而是把整个厨房动线设计成一张蓝图:哪里放灶台、哪里备菜、传菜路径怎么走。TensorRT 构建动态 Shape 引擎同样遵循“先设计、后营业”的思路——构建(Build)阶段把网络结构、Profile 区间、工作空间配置全部固化,产出一个序列化引擎文件,之后部署就靠加载这个“菜谱蓝图”。

构建流程可以拆成四步:先创建 Builder 构建器,它相当于总负责人;再创建 Network 定义模型的计算图,即每道菜的加工步骤;接着创建 Optimization Profile 并绑定到 Builder Config 配置上,相当于把“取餐区间”写进营业手册;最后调用构建接口,产出序列化的引擎字节流。

构建好的引擎像一本“中央厨房手册”:里面的 Profile 区间、内核选择策略、显存规划统统写死。部署时只需加载这本手册,不必重新建厨房——这保证了动态 Shape 引擎在启动后能快速响应不同尺寸的请求。

engine=Serialize(Build(network,  profile,  config))\text{engine} = \text{Serialize}\left( \text{Build}\left( \text{network},\; \text{profile},\; \text{config} \right) \right)
一句话:引擎是网络结构、动态区间和构建配置三者经过 Build 加工、再 Serialize 打包后的最终产物。
  1. network 网络定义模型层与张量连接的完整描述,是“菜谱”本身。
    network=模型计算图\text{network} = \text{模型计算图}
  2. profile 动态区间声明把每个动态维度的 min/opt/max 写入配置,相当于划定餐台的伸缩范围。
    profile  →  [min,  opt,  max]\text{profile} \;\rightarrow\; \left[ \text{min},\; \text{opt},\; \text{max} \right]
  3. config 构建配置包含 Profile 列表、workspace 显存上限等构建期选项。
    config∋{profile,workspaceLimit}\text{config} \ni \left\{ \text{profile}, \text{workspaceLimit} \right\}
  4. Serialize 序列化输出把构建好的引擎压缩成二进制字节流,便于存盘和加载。
    engine  ↦  bytes\text{engine} \;\mapsto\; \text{bytes}

运行时绑定

客人落座后开始点菜,服务员把具体需求报给后厨。动态 Shape 引擎在运行时也需要这样一次“点菜”:每次推理前,你必须通过执行上下文(Execution Context) 告诉引擎当前这次请求的实际输入形状,这一步叫作维度绑定。

执行上下文可以理解为引擎的“当前工作状态实例”。同一份引擎可以创建多个上下文,就像一本菜谱可以供多个厨师同时照做。每个上下文有自己独立的一组绑定缓冲区:set_input_shape 指定输入张量的实际维度,get_tensor_shape 查询输出张量的形状。关键约束是:实际维度必须落在构建时声明的 Profile 区间内。

绑定维度不是每次都要从零开始——如果下一次请求的长度和上一次相同,引擎会复用现成的内部状态,就像熟客“老样子”一样省事。一旦换了个新长度,引擎内部会自动切换对应的内核与显存布局,整个过程对上层透明。这就是动态 Shape“一个引擎、百变输入”的奥秘所在。

di actual  ∈  [  mini,  maxi  ]d_i^{\,\text{actual}} \;\in\; \left[\; \text{min}_i,\; \text{max}_i \;\right]
运行时填的每个实际维度,都必须在 Profile 为它圈定的围栏里;超出就会报错,就像客人点的菜超出了餐台能提供的范围。
  1. 实际维度变量 d指第 i 个输入张量本次推理的真实尺寸。
    diactuald_i^{\text{actual}}
  2. 区间约束实际值不得小于 min 也不得大于 max。
    mini≤di≤maxi\text{min}_i \le d_i \le \text{max}_i
  3. 绑定动作通过上下文的 set 接口把实际维度写到引擎指定位置。
    context.setShape(i,diactual)\text{context.setShape}(i, d_i^{\text{actual}})

内存池管理

自助餐厅的备料仓库不会按最大客流量给每道菜都备满,而是根据时间段统筹:凉菜区和热菜区的备料高峰错开,同一批食材在不同时段被不同餐台使用。TensorRT 构建动态 Shape 引擎时,对显存的管理也遵循同样的“复用哲学”——Workspace 内存池。

模型推理时会产生大量中间张量,这些张量在计算图中各有各的“存活时间”:有的在层 A 算完就死了,有的要活到层 Z。如果每个张量都独享一块显存,内存占用会随动态 Shape 的最大尺寸爆炸式增长。TensorRT 在构建期做了一次“仓库规划”:把生命周期不重叠的张量安排到同一块显存区域轮流使用。

这套复用策略让动态 Shape 引擎的显存占用不再随最大维度线性放大:只要中间张量寿命错开,就能共享仓库空间。实际部署中,你可以通过 set_memory_pool_limit 为 workspace 设置上限,TensorRT 会在上限内自动压缩和复用——就像一个贴着预算经营的厨房主厨,用巧妙的排班代替买更多的仓库。

Mpool=max⁡t  ∑b∈Btsize(b)M_{\text{pool}} = \max_{t} \; \sum_{b \in \mathcal{B}_t} \text{size}\left( b \right)
内存池的总需求,等于计算过程中任意时刻“同时存活”的张量大小之和的最大值;不重叠的张量共用空间。
  1. t 时刻截面想象在计算过程的每一瞬间都拍一张快照。
    t∈[0,Tinference]t \in [0, T_{\text{inference}}]
  2. 存活张量集合 B_t这张快照里还“活着”(仍被后续层需要)的全部张量。
    Bt={张量∣在时刻t仍有效}\mathcal{B}_t = \{ \text{张量} \mid \text{在时刻} t \text{仍有效} \}
  3. 求和与取最大每个时刻都算一次内存总量,再取所有时刻中的峰值。
    Mpool=max⁡t∑b∈Btsize(b)M_{\text{pool}} = \max_{t} \sum_{b \in \mathcal{B}_t} \text{size}(b)

性能优化策略

同样是炒一盘青菜,大厨会用猛火灶快炒,炖汤则换文火炉慢煨。TensorRT 内部维护着大量针对不同形状、不同硬件优化的内核(Kernel),构建动态 Shape 引擎时,它会在 Profile 区间上做的事比你想的更聪明:不是含糊地用一套通用实现凑合,而是针对 opt 维度进行基准测试,为不同尺寸区间挑选不同的内核策略。

当某个输入形状恰好等于 opt 时,引擎直接使用那套精挑细选的最快内核;当形状偏离 opt 时,引擎会退而求其次,从备用内核池中选一个对该尺寸相对高效的选择。这个选择过程被建模为一个最大化问题:从内核集合里,挑出在给定 shape 和硬件下推理速度最快的那个。

实践中的调优秘诀是:把 opt 设成线上出现频率最高的输入形状,让最快内核服务于最常见的场景;同时不要让 Profile 区间过宽,否则引擎需要在“照顾少数派形状”上花费过多优化预算。正如好主厨不会为了偶尔的百人宴把整间厨房都换成大锅。

k∗=arg⁡max⁡k∈K  vk(shape)k^{*} = \arg\max_{k \in \mathcal{K}} \; v_k\left( \text{shape} \right)
最优内核是“速度军备竞赛”的冠军:每个候选内核 k 在 shape 下都有一个速度值 v,谁最快选谁。
  1. 候选内核集合 KTensorRT 为每层算子准备的多套实现,包括不同向量化宽度、不同分块策略。
    K={k1,k2,…,km}\mathcal{K} = \{ k_1, k_2, \ldots, k_m \}
  2. 速度函数 v_k衡量内核 k 在特定 shape 和 GPU 型号下的吞吐,单位通常是 ms 或 samples/s。
    vk(shape)=内核 k 处理该形状的耗时v_k(\text{shape}) = \text{内核 } k \text{ 处理该形状的耗时}
  3. argmax 选择在所有候选中挑选速度值最大的那个。
    k∗  ←  arg⁡max⁡kvkk^* \;\leftarrow\; \arg\max_{k} v_k

部署应用场景

自助餐厅开业后,客流高峰时百人排队,闲时三三两两——但厨房只要开一次门,就能服务所有客人。动态 Shape 在真实部署中扮演的正是这个“一次构建、长时服役”的角色。以 NLP 推理服务为例,用户的 query 短则几个 token、长则上千 token,静态引擎要么按上限 padding 浪费显存,要么为各种长度维护一大堆引擎文件。

动态 Shape 让一个引擎覆盖全部长度区间,服务端只需在收到每个请求后调用上下文绑定接口,把实际 seq_len 传进去即可。对于 LLM 这类 token 逐次增长的解码场景,动态 Shape 还能配合 KV Cache 的增量更新,让显存利用率跟随实际序列长度平滑伸缩,而不是永远按最坏情况预留。

在实际推理集群中,动态 Shape 更带来资源调度的灵活性:多个模型的引擎可以共享 GPU 上的显存池,因为每个引擎的内存需求会随输入尺寸动态伸缩;不同租户的请求长短不一,也被自然地平均到了资源曲线上。就像自助餐线根据客流自动补菜,后厨始终忙碌而从容——这就是动态 Shape 从“能用”走向“好用”的工程价值。

Throughput=NcompletedTwindow\text{Throughput} = \frac{N_{\text{completed}}}{T_{\text{window}}}
吞吐量等于时间窗口内完成的请求数除以窗口时长:动态 Shape 让单位时间内能处理的请求密度更高,因为不再为 padding 白付算力。
  1. 完成的请求数 N在一个统计窗口里,推理服务成功返回结果的次数。
    NcompletedN_{\text{completed}}
  2. 时间窗口 T统计持续的时间长度,比如 1 分钟或 1 小时。
    TwindowT_{\text{window}}
  3. 两者相除得到单位时间吞吐率,是推理服务最核心的运维指标。
    Throughput=NT\text{Throughput} = \frac{N}{T}

AI 应用场景

NLP 变长请求的在线推理服务

阶段:模型推理与部署

解决的问题:传统静态引擎要求输入固定长度,但真实用户 query 长短不一。若按最大长度 padding,Batch 内大部分是无效算力;若为每种长度建引擎,文件与显存开销不可管理。动态 Shape 用一个引擎覆盖所有长度,请求到达时按实际 seq_len 绑定维度,显著降低延迟与显存占用。

具体案例:某智能客服系统每天处理百万级用户 query,长度分布从 8 到 200 token 不等。部署动态 Shape 引擎后,GPU 显存占用降低约 40%,平均延迟降低约 25%,且不再需要为不同长度维护多个引擎版本。

多租户共享 GPU 的智能调度

阶段:资源管理与性能优化

解决的问题:多个业务方共用一块 GPU,各自输入尺寸差异极大。静态引擎需要为每个租户预留最大显存,导致资源碎片化。动态 Shape 引擎的显存需求跟随实际输入伸缩,让调度器更精准地装箱和超卖。

具体案例:某云厂商的推理平台让搜索、推荐、OCR 三个模型共享 L4 GPU。改用动态 Shape 后,同块 GPU 上可同时驻留的模型实例数从 2 个提升到 5 个,整体吞吐提升约 60%。

Python 演示

python46 行
import tensorrt as trtimport numpy as np LOGGER = trt.Logger(trt.Logger.WARNING)  def build_dynamic_engine(min_len, opt_len, max_len):    builder = trt.Builder(LOGGER)    network = builder.create_network(        1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)    )    profile = builder.create_optimization_profile()     # 输入 x = (batch=1, seq_len=动态, hidden=64),NLP场景的典型布局    x = network.add_input("x", trt.float32, (1, -1, 64))    identity = network.add_identity(x)    out = identity.get_output(0)    network.mark_output(out)     # 关键:为动态维度声明min/opt/max三段区间    profile.set_shape("x", (1, min_len, 64),                           (1, opt_len, 64),                           (1, max_len, 64))    config = builder.create_builder_config()    config.add_optimization_profile(profile)    config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 1 << 30)     return builder.build_serialized_network(network, config)  def infer_with_dynamic_shape(engine_bytes, seq_len):    runtime = trt.Runtime(LOGGER)    engine = runtime.deserialize_cuda_engine(engine_bytes)    context = engine.create_execution_context()    # 运行时按请求实际长度绑定维度,无需重建引擎    context.set_input_shape("x", (1, seq_len, 64))    # 实际项目中还需用pycuda分配device显存并拷贝输入数据    shape = context.get_tensor_shape("x")    return "引擎就绪,当前绑定输入维度:%s" % (shape,)  if __name__ == "__main__":    engine = build_dynamic_engine(min_len=8, opt_len=64, max_len=256)    print("动态引擎构建成功,大小(MB):%.2f" % (len(engine) / 1e6))    print(infer_with_dynamic_shape(engine, seq_len=48))    print(infer_with_dynamic_shape(engine, seq_len=128))
目的:演示如何用 TensorRT Python API 构建一个带动态 Shape 的引擎,并在运行时按不同序列长度绑定维度执行推理。
  1. network.add_input("x", trt.float32, (1, -1, 64))定义输入张量,用 -1 声明序列长度为动态维度。
  2. profile.set_shape("x", (1, min_len, 64), (1, opt_len, 64), (1, max_len, 64))为动态维度指定 min/opt/max 三段区间,这是动态 Shape 的核心配置。
  3. config.add_optimization_profile(profile)把 Profile 挂到构建配置上,引擎构建时才会识别动态区间。
  4. builder.build_serialized_network(network, config)执行构建并直接产出序列化引擎字节流。
  5. context.set_input_shape("x", (1, seq_len, 64))运行时绑定本次请求的实际维度,引擎内部自动切换内核。
  6. context.get_tensor_shape("x")查询绑定后的张量形状,确认动态维度生效。

常见误区

误区很多初学者以为把输入定义为 `(-1, -1, 64)` 或者什么都不用做,TensorRT 就会自动支持任意形状的输入。

正解动态 Shape 必须显式做两件事:一是构建前定义 Optimization Profile 的 min/opt/max 区间,二是运行时用 set_input_shape 绑定实际维度。少了 Profile,引擎根本不知道动态维度的边界;少了运行时绑定,引擎无法得知本次输入到底多长。就像自助餐必须先划好餐台范围、客人来了还得报饭量。

为何常见:PyTorch 等框架中动态维度几乎是“默认行为”,很容易让人误以为推理引擎也会自动跟随。

误区有人认为动态引擎要兼容各种尺寸,内核肯定是“大而全”的通用实现,性能一定不如专门为固定尺寸调优的静态引擎。

正解通过合理设置 opt 维度,TensorRT 会为最常出现的形状专门挑选最优内核;当输入恰好等于 opt 时,性能与静态引擎几乎一致。其他尺寸虽然略逊,但省去了多引擎切换和重建的开销。多个静态引擎频繁切换的额外损耗,往往比动态引擎的微小性能差距更致命。

为何常见:只看到“动态=通用=妥协”的直觉,忽略了 TensorRT 在构建期针对 opt 做过定向优化。

误区为了让模型“什么输入都能接”,把 min 设到 1、max 设到 1 万,觉得区间越宽越安全。

正解Profile 区间过宽会带来两个问题:一是显存按 max 预留,过大的 max 导致空闲时也占着大量显存;二是内核优化预算被摊薄,远离 opt 的形状性能会下降。正确的做法是统计线上输入分布,让 min/max 恰好覆盖真实数据,opt 对准高频中心。

为何常见:“覆盖全部可能性”的防御心态,忽略了显存与性能是需要用数据说话的工程权衡。

误区认为动态 Shape 只能在 NLP 场景用,动态维度只能放在 seq_len 上。

正解任何维度都可以声明为动态——包括 batch 维度、图片宽高、通道数。视频流检测中输入分辨率可能变化,推荐系统的 batch 大小可能随流量波动,这些都可以通过动态 Shape 统一承载。-1 只是一个占位符,放在了哪个位置,哪个位置就获得了伸缩能力。

为何常见:教程与案例多围绕 NLP 展开,容易把“常见用法”误当成“唯一用法”。

面试题

Q1. 动态Shape相比静态Shape的最大价值是什么?

单选题 · easy

  • A.减少模型参数量
  • B.免每种尺寸建引擎← 正确答案
  • C.提升单次推理精度
  • D.减少训练数据量

答案:B。动态Shape的核心价值是让一个引擎服务所有输入尺寸,避免为每个形状单独构建和部署引擎,就像一间自助厨房服务所有饭量的客人。它不改变模型参数、不提升精度,也与训练数据无关。

Q2. 优化Profile的三个关键维度是什么?

单选题 · easy

  • A.low/mid/high
  • B.min/opt/max← 正确答案
  • C.min/avg/max
  • D.start/peak/end

答案:B。Profile 用 min(下界)、opt(最优档位)、max(上界)三段区间描述动态维度的合法范围,其中 opt 是内核调优的基准。其他三组词都不是 TensorRT 中 Profile 的标准术语。

Q3. 关于优化Profile的说法正确的有哪些?

多选题 · mid

  • A.opt维度用于指导内核选择← 正确答案
  • B.输入必须落在min到max区间内← 正确答案
  • C.引擎只能绑定一个Profile
  • D.运行时可以重新绑定维度← 正确答案

答案:ABD。opt 是内核挑选的基准,实际维度必须在 [min, max] 内,运行时每次推理都可以重新绑定——这三点都对。但一个引擎可以绑定多个 Profile(例如不同 batch 范围),C 错在“只能绑定一个”。

Q4. 输入定义为(1, -1, 64)时,-1表示什么?

单选题 · mid

  • A.该维度输入为空
  • B.该维度大小可变← 正确答案
  • C.该维度无任何限制
  • D.该维度固定为零

答案:B。-1 是动态维度占位符,表示这个维度在构建时不定死,运行时通过 set_input_shape 填入具体值。它并非“空”或“零”,也并非完全无限制——必须受 Profile 的 min/max 约束。

Q5. 运行时输入长度超过Profile的max会怎样?

单选题 · mid

  • A.自动扩容引擎
  • B.报错或行为不正确← 正确答案
  • C.自动裁剪到max值
  • D.静默重建引擎

答案:B。超出 max 的输入无法被引擎正确处理,通常会导致错误。引擎构建时显存与内核都是按区间规划的,超界输入无法保证安全性;TensorRT 不会悄悄裁剪或重建引擎。

Q6. 动态Shape的显存优化包含哪些?

多选题 · mid

  • A.workspace统一配置上限← 正确答案
  • B.生命周期不重叠的张量共享显存← 正确答案
  • C.每个输入独立分配大块显存
  • D.构建期规划全局内存复用← 正确答案

答案:ABD。TensorRT 通过 workspace 上限约束总显存,并在构建期把生命周期不重叠的中间张量安排到同一区域复用。C 恰恰是静态引擎的做法,违背显存复用哲学,会导致占用随动态尺寸爆炸。

Q7. 动态Shape引擎的构建流程正确顺序是什么?

单选题 · mid

  • A.定义网络→创建Profile→构建→序列化← 正确答案
  • B.序列化→定义网络→创建Profile
  • C.创建Profile→序列化→定义网络
  • D.构建→定义网络→序列化→创建Profile

答案:A。构建动 Shape 引擎必须先定义网络计算图,再创建并绑定 Profile,然后执行构建,最后序列化保存。其他顺序在逻辑上不通,因为序列化必须发生在构建完成之后。

Q8. opt维度最合适的取值策略是什么?

单选题 · hard

  • A.取min和max的几何中点
  • B.取线上最频繁出现的形状← 正确答案
  • C.永远取区间的最大值
  • D.任意值,不影响性能

答案:B。opt 是 TensorRT 内核调优的靶心,应设为线上分布的高频中心,让最常见请求跑在最快内核上。中点和最大值都不符合数据分布,任意值更是会错失优化机会。

Q9. 一个引擎绑定多个Profile相比单个Profile的优势是?

单选题 · hard

  • A.减少构建时间
  • B.同时服务多段不同的尺寸区间← 正确答案
  • C.完全消除运行时绑定
  • D.自动增加模型精度

答案:B。多个 Profile 可以分别覆盖不同区间的形状,例如一个覆盖小 batch 高延迟敏感、一个覆盖大 batch 高吞吐场景,运行时按需选择。它不减少构建时间,也不消除维度绑定,更与模型精度无关。

Q10. 部署动态Shape引擎时应用层需要做哪些事?

多选题 · hard

  • A.按请求实际维度调用绑定接口← 正确答案
  • B.确保维度落在Profile区间内← 正确答案
  • C.关闭显存池配置以省内存
  • D.为并发请求创建多个执行上下文← 正确答案

答案:ABD。应用层要负责把每个请求的真实形状绑定到上下文,并保证它合法;高并发时通常创建多个上下文并行执行,让每个请求独立绑定。C 恰恰相反,合理配置 workspace 显存池正是动态 Shape 省显存的关键手段。