Mini-Infer (25): 动态形状的基石 — `OptimizationProfile` 设计与实现
Mini-Infer (25): 动态形状的基石 — OptimizationProfile 设计与实现
1. 核心理念:Min, Opt, Max 三位一体
对于任何一个动态输入(比如 input_0),我们不再只给它一个模糊的 -1,而是要求用户提供三个形状:
- Min Shape: 允许的最小尺寸。
- 作用:边界检查。小于此尺寸的输入将被拒绝。
- Opt Shape (Optimal): 最常用的尺寸。
- 作用:核心优化的依据。引擎会针对这个尺寸选择最优的算法(Kernel Selection)和执行调度策略。
- Max Shape: 允许的最大尺寸。
- 作用:内存分配的依据。
MemoryPlanner会根据 Max Shape 来计算网络中所有中间 Tensor 的最大可能尺寸,并据此分配显存。这样,在运行期间只要输入不超过 Max,就永远不需要重新分配显存。
- 作用:内存分配的依据。
2. 代码实现:ShapeRange 与 OptimizationProfile
A. 形状范围 (ShapeRange)
这是 Profile 的基本单元。它封装了三个形状,并提供了校验逻辑。
1 | struct ShapeRange { |
B. 配置文件管理器 (OptimizationProfile)
这个类管理整个网络所有输入的范围设置。它就像是引擎的“合同”,用户必须遵守。
1 | class OptimizationProfile { |
3. 如何集成到 Engine 工作流中?
OptimizationProfile 是连接 Build Time (编译期) 和 Run Time (运行期) 的桥梁。
编译期 (Engine::build)
当我们在编译网络时,我们会查询 profile->get_optimal_shapes()。
- Kernel 选择器 会用这组形状去测试不同的卷积算法,选出最快的一个。
- Memory Planner 会查询
max形状(虽然代码中展示的是opt,但通常显存分配要看max),计算出整个推理过程所需的最大显存块,并预先申请。
运行期 (Engine::forward)
当用户调用 forward 时:
- 校验:调用
profile->is_valid_for(input_shapes)。如果输入超出了[min, max]范围,直接报错,防止显存越界。 - 推理:调用我们在 Blog 24 中实现的
ShapeInferenceEngine,计算当前具体的输出形状。 - 执行:使用预分配好的(足够大的)显存执行计算。
4. 为什么这很重要?
在实际生产环境中,稳定性 (Stability) 压倒一切。
如果没有 OptimizationProfile:
- 用户输入变大 -> 触发
realloc-> 耗时增加,甚至导致系统其他进程因 OOM 崩溃。 - 用户输入变小 -> 显存没有释放 -> 浪费资源。
有了 OptimizationProfile:
- 显存零波动:无论输入怎么变(只要在范围内),显存占用是恒定的(Max Profile 决定)。
- 性能可预期:我们在最常用的尺寸(Opt Profile)上做了极致优化。
5. 总结
OptimizationProfile 是 Mini-Infer 支持动态形状的最后一块基石。它体现了 “Bounded Dynamism” (有界动态) 的设计思想——给予用户灵活性,但这种灵活性必须被限制在系统可控的范围内。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 James的成长之路!
评论





