3. 多模型推理调度问题建模

在 STM32N6 上部署多模型推理时,真正困难的部分往往不是单个模型能否跑通,而是多个模型共享 NPU、DMA、外部内存与 CPU 时,如何确定“谁先跑、谁等谁、谁和谁不能同时跑”。要回答这些问题,必须先把工程直觉翻译成可计算的形式化模型。本节围绕五个要素展开:模型集合、输入输出依赖、实时性约束、资源约束与调度目标,并给出可直接用于调度器设计的符号描述。

一、模型集合与任务抽象

设应用中共有 N 个待执行模型,记为集合 M = {m₁, m₂, …, m_N}。每个模型 mᵢ 在调度层面被抽象为一个任务 τᵢ,其属性可写成五元组:

τᵢ = (Pᵢ, Cᵢ, Dᵢ, Tᵢ, Rᵢ)
Pᵢ : 优先级 / 关键等级
Cᵢ : 最坏执行时间 WCET(NPU 周期或 ms)
Dᵢ : 相对截止期(从就绪到必须完成)
Tᵢ : 触发周期(周期任务;非周期任务记为 ∞)
Rᵢ : 资源需求向量(NPU、DMA、AXI-SRAM、外部 PSRAM 等)

以典型 STM32N6 视觉应用为例:m₁ 为 320×320 的人脸检测模型,C₁ ≈ 18 ms,周期 T₁ = 33 ms;m₂ 为 112×112 的人脸识别模型,C₂ ≈ 9 ms,由检测结果触发,属非周期任务;m₃ 为 1D 语音关键词模型,C₃ ≈ 4 ms,周期 T₃ = 20 ms。这些数值属于教学示例,实际应以 STM32Cube.AI 生成的性能报告为准。

二、输入输出依赖:DAG 建模

模型之间并非彼此独立。人脸识别必须等待检测框输出,语音唤醒后的命令词识别必须等待唤醒事件。这类依赖用有向无环图 G = (M, E) 描述,边 (mᵢ, mⱼ) ∈ E 表示 mⱼ 的输入张量由 mᵢ 的输出产生。对每条边还需标注数据量与传输路径:

e = (mᵢ → mⱼ, size, path)
size : 张量字节数,如 1×128 float32 = 512 B
path : SRAM 直传 / DMA 搬运 / PSRAM 中转

依赖关系直接决定就绪条件:mⱼ 只有在所有前驱 pred(mⱼ) 完成后才可进入就绪队列。若把依赖忽略,调度器可能让识别模型读到上一帧的旧特征,产生难以复现的精度抖动。

多模型依赖图与资源占用示意 m₁ 人脸检测 C=18ms T=33ms NPU + DMA m₂ 人脸识别 C=9ms 事件触发 NPU + PSRAM m₃ 语音唤醒 C=4ms T=20ms NPU + SRAM m₄ 命令词识别 C=6ms 事件触发 NPU + SRAM 检测框 512B 唤醒事件 共享资源池 NPU(单核,串行) DMA 通道 ×2 AXI-SRAM 1.4MB 外部 PSRAM 带宽 依赖边决定就绪顺序;NPU 与内存带宽构成互斥约束

三、实时性约束

实时性约束分两类。第一类是截止期约束:任务 τᵢ 的第 k 次实例必须在 rᵢ,k + Dᵢ 前完成,其中 rᵢ,k 为释放时刻。第二类是端到端时延约束:对依赖链 m₁ → m₂,从摄像头帧就绪到识别结果输出必须小于 L_max,例如 L_max = 50 ms。端到端约束不能简单拆成各任务独立截止期,因为链上任意一段的排队延迟都会累积。

工程上常用“松弛度”判断可行性:slackᵢ = Dᵢ − Cᵢ − 等待时间。若某周期内所有任务的最小松弛度均非负,则该调度可行。对周期任务还需满足利用率条件 Σ(Cᵢ / Tᵢ) ≤ 1;示例中 18/33 + 4/20 ≈ 0.745,NPU 仍有约 25% 余量,但一旦叠加事件触发的 m₂、m₄,瞬时利用率可能超过 1,必须靠优先级或时间片化解。

四、资源约束

STM32N6 的关键资源包括:单个 NPU 加速器(同一时刻只能执行一个模型)、有限 DMA 通道、AXI-SRAM 容量、外部 PSRAM 带宽以及 CPU 中断处理能力。资源约束可统一写成容量型与互斥型两种:

  • 容量型:Σ size(激活张量驻留) ≤ SRAM_CAP,超出则必须换出到 PSRAM,引入额外搬运时间。
  • 互斥型:NPU 同一时刻仅允许一个任务占用,即对任意 i ≠ j,执行区间 [sᵢ, fᵢ) 与 [sⱼ, fⱼ) 不得重叠。
  • 带宽型:Σ bwᵢ(t) ≤ BW_PSRAM,当多个模型同时做权重读取时会互相拖慢。

把互斥约束显式写进模型,调度器才能在“检测未完成时不让识别抢占 NPU”这一层面做出正确决策,而不是依赖运行时偶然的先后顺序。

五、调度目标

调度目标决定优化方向,常见有三种,需按应用取舍:

目标形式化表达适用场景
最小化端到端时延min max(fⱼ − rᵢ)(沿依赖链)交互式视觉、手势响应
最大化截止期满足率max Σ [fᵢ ≤ dᵢ] / N混合周期与事件任务
最小化 NPU 空闲与切换min(空闲时间 + 上下文切换次数)功耗敏感、持续推理

实际工程通常采用加权组合:J = α·时延 + β·超时惩罚 + γ·切换开销,权重由产品指标确定。例如安防场景更看重不丢帧,β 取大;电池供电场景更看重能效,γ 取大。

建模完成后应先做可行性检查:计算 NPU 利用率、检查依赖链端到端时延、核对 SRAM 峰值占用。三项中任一项越界,都应回到模型集合或依赖图调整,而不是直接进入编码。

六、从形式化到可执行描述

把上述要素整理成调度器可直接读取的结构,可用如下伪结构表达(示例):

tasks = [
  {id:"m1", C:18, T:33, D:33, res:["NPU","DMA"], pred:[]},
  {id:"m2", C:9,  T:INF,D:25, res:["NPU","PSRAM"], pred:["m1"]},
  {id:"m3", C:4,  T:20, D:20, res:["NPU","SRAM"], pred:[]},
  {id:"m4", C:6,  T:INF,D:20, res:["NPU","SRAM"], pred:["m3"]}
]
objective = 0.5*latency + 0.4*deadline_miss + 0.1*switch_cost

这份描述同时覆盖了模型集合、依赖、实时性、资源与目标五个维度,后续无论是实现静态优先级调度、时间触发调度,还是基于就绪队列的动态调度,都可以在此基础上展开。建模的价值在于:把“感觉上应该先跑检测”变成“依赖图与截止期共同推出的必然顺序”,使多模型推理在 STM32N6 上的行为可预测、可验证、可复现。