searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

NVIDIA Dynamo 动态 PD 分离技术实现

2026-10-16 17:34:55
1
0

一、背景

1.1 PD 分离及其配比困境

LLM 推理的一个请求生命周期包含两个阶段:Prefill 阶段一次性并行处理全部输入 token,计算密集、单次前向耗时长,直接决定首 token 延迟(TTFT);Decode 阶段逐个生成输出 token,访存密集、单步前向耗时短,决定 token 间延迟(ITL)。两类负载在同一引擎内混合调度会互相干扰——长的 Prefill 计算会阻塞同批 Decode 步进,导致 ITL 抖动。

PD 分离(Prefill-Decode Disaggregation)将两类负载拆分到独立副本组,Prefill 副本计算完后将 KV Cache 传输至 Decode 副本继续生成,从而各自按特征调优。随之而来的问题是配比决策:Prefill 与 Decode 副本数量应为多少?离线 profiling 得到的静态配比建立在特定 ISL/OSL(输入/输出序列长度)分布假设之上,一旦线上流量模式变化(如提示词显著变长、突发请求涌入),静态配比要么让 Prefill 过剩、Decode 欠载造成 GPU 闲置,要么反向失衡导致 TTFT 或 ITL 违约。

1.2 动态 PD 分离的定义

本文所讨论的"动态 PD 分离",指系统能够依据实时负载状态,在不中断服务的前提下自动调整 Prefill 与 Decode 两组副本的数量配比,在满足延迟 SLA 约束的同时最小化 GPU 占用。它包含三个核心子问题:

1. 感知:如何以足够低的开销和足够高的频率获得引擎内部的真实负载状态;

2. 决策:如何将负载状态映射为副本数调整动作,且决策需对资源约束、最小副本数、正在进行的扩缩容等条件鲁棒;

3. 执行:如何将决策可靠地落地到 Kubernetes 等基础设施,并保证调度安全。

Dynamo 的 planner 组件对这三个子问题分别给出了工程化的答案。

二、总体架构:Planner 与连接器

2.1 部署形态与四种模式

planner 是一个独立运行的守护进程,通过 python -m dynamo.planner 启动,入口位于 __main__.py,由 construct_planner 按运行模式装配。源码提供了四种模式,对应 core/adapters.py 中的四个适配器子类:

• prefill / decode:只管辖单侧副本(用于仅一侧需要弹性调整的场景);

• agg:管辖混合负载副本组(聚合模式);

• disagg:PD 分离模式,同时管辖 Prefill 与 Decode 两组副本,是本文的主角。

四种模式共享同一套决策内核,仅观测对象与决策分量不同,体现了"机制与策略分离"的设计取向。

2.2 决策落地链路

planner 的核心决策产物是一个二元组 ScalingDecision(num_prefill, num_decode)——目标 Prefill 与 Decode 副本数。决策本身不直接操作 Pod,而是经连接器(Connector)落地:

• KubernetesConnector:生产环境默认连接器。通过 set_component_replicas 将决策 PATCH 到自定义资源 DGD(DynamoGraphDeployment CRD);deploy/operator/ 下的 Go Operator 监听 DGD 变更,调和派生出 StatefulSet 等底层资源,完成副本增减。

• VirtualConnector:非 Kubernetes 环境(如裸机测试)使用,仅维护虚拟组件副本计数;

• GlobalPlanner:跨 DGD 的全局 GPU 预算协调器,在多个部署之间分配 GPU 配额。

这一分层使决策算法完全与基础设施解耦:算法只关心"应该有几个副本",而"如何安全地变成几个副本"(PDB、亲和性、优雅驱逐等)交由 Operator 生态处理。

三、双循环扩缩容:快与慢的协同

planner 最核心的设计是将扩缩容决策拆分为频率与逻辑都不同的两个循环,源码位于 core/throughput_scaling.py 与 core/load_scaling.py。

3.1 慢循环:吞吐预测定下界

慢循环(Throughput-based scaling)以 Prometheus 采集的流量指标为输入:请求速率、平均 ISL/OSL、KV 命中率、投机解码 accept length 等。其决策链为:

流量指标 → 负载预测器 → 性能模型 → 需求副本数

负载预测器(core/load/predictors.py)提供 Constant、ARIMA、Kalman、Prophet 四种实现,对未来负载区间做出预测;性能模型 find_engine_capacity_rps 计算单引擎在 SLA 内可承载的每秒请求数(RPS);两者相除向上取整即得所需副本数。

关键设计在于:慢循环的产物不直接成为最终决策,而是作为下界(_throughput_lower_bound_p/d)参与后续合成。理由是流量指标本身就是周期平均的产物,采样窗口内的瞬时拥塞会被平均掉——若只按平均吞吐配副本,波峰期间必然违约。以预测容量做下界,相当于为波峰预留了保护垫。

3.2 快循环:负载响应做微调

快循环(Load-based scaling)的输入是 FPM(ForwardPassMetrics):推理引擎在每次前向迭代时通过 ZMQ 上报到事件平面的实时遥测,包含调度中队列长度、各队列的 token 求和、投机解码接受长度等。相比 Prometheus 的秒级窗口聚合,FPM 是迭代级的高频信号,能捕捉到排队拥塞的形成过程。

快循环的处理逻辑:将 FPM 快照喂给在线性能回归模型,估算各引擎当前的 TTFT/ITL,与 SLA 目标(配置项 ttft_ms / itl_ms)比较——所有引擎的估算 TTFT(或 ITL)均超出 SLA 则 +1,持续低于阈值则尝试 -1。每个决策周期最多调整一个副本,以渐进方式逼近配比,避免过冲。

3.3 两循环的合成

最终决策按"快循环为主、慢循环为底"合成:快循环的负载微调结果先经过慢循环下界抬升(max(load_decision, throughput_lower_bound)),再叠加最小副本数约束、GPU 预算带与功耗预算限制(详见 3.5 节)。这种设计让两个循环各司其职——慢循环负责"从趋势上不错配",快循环负责"对当下变化快速反应"。

3.4 优化目标:optimization_target

慢循环是否启用以及 SLA 语义由 optimization_target 配置决定,四种取值对应不同策略形态:

• sla(默认):启用回归模型估算 TTFT/ITL 与 SLA 比较,前述主路径;

• throughput:以吞吐容量为决策依据,不依赖延迟估算;

• latency(即"easy mode"):绕开回归模型,使用静态阈值——Prefill 侧以排队 token 数占上下文长度的比例为信号,Decode 侧以 KV 利用率为信号。性能模型样本不足时的安全退路;

• load:完全由用户自定义阈值驱动,最大化灵活性。

3.5 约束叠加链

决策在生效前依次通过一条约束钳制链(core/budget.py):

1. 最小副本数(min_endpoint):每侧副本不得低于下限;

2. GPU 预算带:通过 proportional_clamp_pair 将两侧副本数按 GPU 配额成比例钳制,容差步长取单副本 GPU 数的最大值,保证钳制结果始终是合法的整数副本组合;

3. 功耗预算(total_gpu_power_limit):预估总功耗不得突破限额;rollout(灰度发布)期间采取 fail-closed 策略——冻结扩容,防止放量与扩容叠加造成功耗越限。

四、性能模型:在线回归与冷启动引导

快循环的核心依赖是一组在线性能回归模型(core/perf_model/),它们必须回答两类反事实问题:"如果队列再长一些,TTFT 会是多少"、"如果批量再大一些,ITL 会是多少"。

4.1 Prefill 一维回归模型

PrefillRegressionModel 以 sum_prefill_tokens → wall_time 的一维线性回归刻画 Prefill 耗时。直接用回归截距当 TTFT 并不充分——真实 TTFT 是排队的前序请求全部完成后才轮到新请求。因此 estimate_queued_prefill_time 按引擎的 max_num_batched_tokens(单次迭代最大可处理 token 数)将排队总量切块,逐块用回归函数累加 wall time,模拟了分块调度的排队演化。KV 命中率(来自慢循环指标)作为折扣因子作用到排队 token 上,上限 0.95,防止命中率异常高时过度乐观。

4.2 Decode 二维回归模型

DecodeRegressionModel 是以 [num_decode_requests, sum_decode_kv_tokens] → wall_time 的二维回归:批量中请求数与 KV cache 总 token 数共同决定一步解码耗时。ITL 估算还需除以投机解码 accept length——一次前向生成多个 token 时,有效 ITL 被成比例摊薄。引擎容量(单副本在 ITL SLA 内可稳定承载的请求数)通过 batch size 二分搜索求得,并以 Little's law(在途请求数 = 到达率 × 停留时间)换算为 RPS 容量。

4.3 样本治理:分桶退避与物理性校验

在线模型的样本质量决定决策质量。两个机制值得注意:

• 分桶退避(bucketed retirement):FPM 样本按输入特征分桶,淘汰时整桶退避而非全局滑窗,保证低频但重要的工况(如超长序列)不会被高频工况淹没;

• 负系数拒绝:回归拟合若产生负斜率(更多 token 反而更快——物理上不可能),该拟合结果被拒绝使用,避免模型外推出荒谬决策。

4.4 冷启动引导:三级数据源优先级

新部署的集群没有任何运行时样本,性能模型不启动则快循环失明。monitoring/perf_metrics.py 中的 fetch_pre_deployment_metrics 实现了按优先级递减的三级引导:

1. worker 自测基准:调用引擎的 get_perf_metrics 端点,让引擎上线时自行跑一组合成基准,生成真实 FPM 样本;

2. AIC 插值(AI Configurator):基于 aiconfigurator_core SDK,依据模型 ID、GPU 型号与后端类型,从内置 profile 数据库插值出合成 FPM(rapid 模式);

3. 离线 profile 数据:加载 profile_results_dir 下预先采集的 NPZ/JSON profiling 数据(thorough 模式)。

三级全部失败则抛出异常拒绝启动(fail-fast);即使引导成功,运行时 FPM 仍会持续积累并最终接管回归拟合。PlannerEnginePerfModel 同时充当 aiconfigurator SDK 的包装器并支持在线修正,使冷热数据无缝衔接。

五、防震荡的缩容安全机制

扩容决策的代价只是"多花一些 GPU",而错误缩容的代价是 SLA 违约与随后的补偿性扩容,形成缩容—扩容震荡。planner 在快循环中实现了 consolidation-aware scale-down(感知合并的缩容),这是源码中最具巧思的部分。

5.1 N/(N-1) 合并重预测

缩容的本质是"把一个副本上的负载合并到其余副本"。候选动作评估时,源码以 consolidation = N/(N-1) 为缩放系数,重预测缩容后幸存副本将承受的负载水平,并执行角色化安全检查。以 Decode 为例(load_scaling.py _decode_load_decision):

• KV 容量硬检查:合并后单副本的调度中 plus 排队 KV token 总量(post_sched_kv)必须小于该引擎 max_kv_tokens。一旦越过 KV 容量边界,请求被迫排队或触发块逐出,进入性能模型无法刻画的非线性区,因此直接整体拒绝缩容;

• SLA 软检查:在合并后的 KV 负载水平下重估 ITL,若超过 itl_ms × sensitivity 则拒绝。该检查与容量检查解耦——实践中引擎常在 KV cache 远未满时延迟就已饱和。

Prefill 的合并检查则聚焦排队诱发延迟:新请求自身的前向耗时 T_own 不随副本数缩小,是不可避免的下界;只有 队列诱发部分 < (ttft_ms - T_own) × sensitivity 才允许缩容(sensitivity 即 load_scaling_down_sensitivity 配置百分比)。被拒绝时记录诊断原因 scale_down_refused_consolidation,运维可从诊断报告直接看到拒绝依据。这套机制使 2→1 这类高风险合并被系统性拦截,从根源上抑制了最常见的小规模震荡模式。

5.2 执行期保护:扩缩窗口与观测对账

除决策期检查外,还有两层运行时保护:

• scaling_in_progress Guard:副本变更从下达到 Operator 调和完成存在分钟级窗口,期间 planner 只观测不决策,避免基于过渡态连发指令;

• FPM 对账(_reconcile_fpm_worker_count):周期性比对 FPM 遥测覆盖的 worker 数与 DGD 实际副本数(含 DP rank 完整性校验)。数据投影引擎缺席意味着观测盲区,此时禁用依赖 FPM 的决策路径。

六、插件化决策流水线

前述双循环作为内置能力运行于一条插件化流水线之上(plugins/ 目录),这是 planner 面向未来扩展的关键架构投资。

6.1 五阶段流水线与合并语义

每个决策 tick 顺序经过四个阶段,EXECUTE 为最终决策投影:

• PREDICT(预测):链式执行(chain_augment)——优先级升序逐个调用,首个产出者胜(first-writer-wins),部分结果沿链传递补全,最终汇入 PipelineContext.predictions;

• PROPOSE(提议):全部插件扇出并发执行,产物经类型感知合并(type_aware_merge);

• RECONCILE(调和):对提议再次扇出过滤、重加权;

• CONSTRAIN(约束):最终钳制阶段,明确禁止 SET(直接指定副本数)类输出——只允许 AT_LEAST/AT_MOST 夹逼与 REJECT,防止约束插件越权改写提议;

• EXECUTE:不为独立阶段,而是将合并产物投影为连接器调用或跳过动作(apply / skip_no_targets / skip_short_circuit / skip_tick_timeout)。

type_aware_merge 的合并语义按类型定义:多条 SET 冲突时优先级最小者胜;AT_LEAST 取最大值作地板、AT_MOST 取最小值作天棚,与 SET 推荐值夹逼出最终目标;任一 REJECT 则整轮短路熔断,直接跳过执行。携带 final=True 标志的提议可完全覆盖其他提议(运行时对非最低优先级使用 final 做检测告警)。

6.2 内置插件

双循环被包装为三个内置插件注册进流水线(engine_adapter.py):

插件

阶段

优先级

职责

builtin_load_predict

PREDICT

0

运行流量预测器,产出 predictions

builtin_load_propose

PROPOSE

10

快循环负载微调提议

builtin_throughput_propose

PROPOSE

20

慢循环吞吐下界提议

两个 PROPOSE 插件合并的结果等价于 3.3 节的"快为主、慢为底"合成——下界语义天然由 AT_LEAST 类型表达。

6.3 节流、熔断与外部扩展

• 执行节拍:流水线基础节拍由 scale_interval_seconds 驱动;每个插件另有 execution_interval_seconds 独立周期(强制不小于基础节拍),未到期的插件以 HOLD_LAST 缓存复用上轮输出,长周期插件(如慢循环)不至于拖慢短周期决策;

• 熔断器:单插件连续失败触发 CircuitBreaker,暂停其调用并隔离故障,防止一个坏插件拖垮整条流水线;

• 外部插件:通过 gRPC Gateway 可在运行期注册外部插件(任意语言实现,FPM 采用 msgpack 跨语言编码),PipelineContext 是唯一输入通道。用户可注入自定义预测器、提议策略或约束条件,与内置逻辑同台合并——这使"动态 PD 策略"从固定算法升级为可编程框架。

七、主循环节奏与工程保障

7.1 tick 编排

planner 主循环(core/base.py 的 NativePlannerBase.run())每个 tick 顺序执行:refresh(刷新配置与集群视图)→ observe(懒拉取 Prometheus 流量、worker 计数与 FPM 快照)→ engine.tick(配置锁内驱动决策流水线)→ apply_effects(下发 ScalingDecision)→ connector.apply_scaling(连接器落地)。全流程与引擎推理路径完全异步,观测数据采集不侵入请求热路径。

7.2 可观测性与试运行

• 全链路 Prometheus 指标(插件调用耗时、合并类型计数、扩缩动作计数);

• DiagnosticsRecorder 输出 HTML 诊断报告,包含每次决策的估算 TTFT/ITL、拒绝原因(如 scale_down_refused_consolidation)、约束钳制轨迹;

• advisory(顾问)模式:只输出决策不实际执行,供新参数上线前的干跑验证。

八、局限与展望

官方设计文档(planner-design.md)坦诚列出了当前实现的边界:

1. 调整间隔与扩容延迟的错配:决策周期可到秒级,但副本冷启动(拉起、加载权重、预热)为分钟级,快循环实际"快"在检测而非生效;突发流量的第一波仍需靠过载缓冲扛过;

2. 平均值预测的天花板:负载预测器输出均值而非分布,对长尾流量形态欠拟合;

3. Local Planner 的管辖范围:当前 planner 决策限于单个 DGD 内部,跨部署的全局配比依赖 GlobalPlanner 协调,其自身仍在演进;

4. 合并假设的理想化:N/(N-1) 均摊假设缩容后负载均匀重分布,未显式建模路由器迁移成本与瞬时不均衡。

这些边界同时勾勒了演进方向:更细粒度的负载分布建模、基于分布的预测、以及与推理引擎协同的原地弹性(如 KV Cache 热迁移减少冷启动代价)。

0条评论
0 / 1000
张****鹏
2文章数
0粉丝数
张****鹏
2 文章 | 0 粉丝
张****鹏
2文章数
0粉丝数
张****鹏
2 文章 | 0 粉丝
原创

NVIDIA Dynamo 动态 PD 分离技术实现

2026-10-16 17:34:55
1
0

一、背景

1.1 PD 分离及其配比困境

LLM 推理的一个请求生命周期包含两个阶段:Prefill 阶段一次性并行处理全部输入 token,计算密集、单次前向耗时长,直接决定首 token 延迟(TTFT);Decode 阶段逐个生成输出 token,访存密集、单步前向耗时短,决定 token 间延迟(ITL)。两类负载在同一引擎内混合调度会互相干扰——长的 Prefill 计算会阻塞同批 Decode 步进,导致 ITL 抖动。

PD 分离(Prefill-Decode Disaggregation)将两类负载拆分到独立副本组,Prefill 副本计算完后将 KV Cache 传输至 Decode 副本继续生成,从而各自按特征调优。随之而来的问题是配比决策:Prefill 与 Decode 副本数量应为多少?离线 profiling 得到的静态配比建立在特定 ISL/OSL(输入/输出序列长度)分布假设之上,一旦线上流量模式变化(如提示词显著变长、突发请求涌入),静态配比要么让 Prefill 过剩、Decode 欠载造成 GPU 闲置,要么反向失衡导致 TTFT 或 ITL 违约。

1.2 动态 PD 分离的定义

本文所讨论的"动态 PD 分离",指系统能够依据实时负载状态,在不中断服务的前提下自动调整 Prefill 与 Decode 两组副本的数量配比,在满足延迟 SLA 约束的同时最小化 GPU 占用。它包含三个核心子问题:

1. 感知:如何以足够低的开销和足够高的频率获得引擎内部的真实负载状态;

2. 决策:如何将负载状态映射为副本数调整动作,且决策需对资源约束、最小副本数、正在进行的扩缩容等条件鲁棒;

3. 执行:如何将决策可靠地落地到 Kubernetes 等基础设施,并保证调度安全。

Dynamo 的 planner 组件对这三个子问题分别给出了工程化的答案。

二、总体架构:Planner 与连接器

2.1 部署形态与四种模式

planner 是一个独立运行的守护进程,通过 python -m dynamo.planner 启动,入口位于 __main__.py,由 construct_planner 按运行模式装配。源码提供了四种模式,对应 core/adapters.py 中的四个适配器子类:

• prefill / decode:只管辖单侧副本(用于仅一侧需要弹性调整的场景);

• agg:管辖混合负载副本组(聚合模式);

• disagg:PD 分离模式,同时管辖 Prefill 与 Decode 两组副本,是本文的主角。

四种模式共享同一套决策内核,仅观测对象与决策分量不同,体现了"机制与策略分离"的设计取向。

2.2 决策落地链路

planner 的核心决策产物是一个二元组 ScalingDecision(num_prefill, num_decode)——目标 Prefill 与 Decode 副本数。决策本身不直接操作 Pod,而是经连接器(Connector)落地:

• KubernetesConnector:生产环境默认连接器。通过 set_component_replicas 将决策 PATCH 到自定义资源 DGD(DynamoGraphDeployment CRD);deploy/operator/ 下的 Go Operator 监听 DGD 变更,调和派生出 StatefulSet 等底层资源,完成副本增减。

• VirtualConnector:非 Kubernetes 环境(如裸机测试)使用,仅维护虚拟组件副本计数;

• GlobalPlanner:跨 DGD 的全局 GPU 预算协调器,在多个部署之间分配 GPU 配额。

这一分层使决策算法完全与基础设施解耦:算法只关心"应该有几个副本",而"如何安全地变成几个副本"(PDB、亲和性、优雅驱逐等)交由 Operator 生态处理。

三、双循环扩缩容:快与慢的协同

planner 最核心的设计是将扩缩容决策拆分为频率与逻辑都不同的两个循环,源码位于 core/throughput_scaling.py 与 core/load_scaling.py。

3.1 慢循环:吞吐预测定下界

慢循环(Throughput-based scaling)以 Prometheus 采集的流量指标为输入:请求速率、平均 ISL/OSL、KV 命中率、投机解码 accept length 等。其决策链为:

流量指标 → 负载预测器 → 性能模型 → 需求副本数

负载预测器(core/load/predictors.py)提供 Constant、ARIMA、Kalman、Prophet 四种实现,对未来负载区间做出预测;性能模型 find_engine_capacity_rps 计算单引擎在 SLA 内可承载的每秒请求数(RPS);两者相除向上取整即得所需副本数。

关键设计在于:慢循环的产物不直接成为最终决策,而是作为下界(_throughput_lower_bound_p/d)参与后续合成。理由是流量指标本身就是周期平均的产物,采样窗口内的瞬时拥塞会被平均掉——若只按平均吞吐配副本,波峰期间必然违约。以预测容量做下界,相当于为波峰预留了保护垫。

3.2 快循环:负载响应做微调

快循环(Load-based scaling)的输入是 FPM(ForwardPassMetrics):推理引擎在每次前向迭代时通过 ZMQ 上报到事件平面的实时遥测,包含调度中队列长度、各队列的 token 求和、投机解码接受长度等。相比 Prometheus 的秒级窗口聚合,FPM 是迭代级的高频信号,能捕捉到排队拥塞的形成过程。

快循环的处理逻辑:将 FPM 快照喂给在线性能回归模型,估算各引擎当前的 TTFT/ITL,与 SLA 目标(配置项 ttft_ms / itl_ms)比较——所有引擎的估算 TTFT(或 ITL)均超出 SLA 则 +1,持续低于阈值则尝试 -1。每个决策周期最多调整一个副本,以渐进方式逼近配比,避免过冲。

3.3 两循环的合成

最终决策按"快循环为主、慢循环为底"合成:快循环的负载微调结果先经过慢循环下界抬升(max(load_decision, throughput_lower_bound)),再叠加最小副本数约束、GPU 预算带与功耗预算限制(详见 3.5 节)。这种设计让两个循环各司其职——慢循环负责"从趋势上不错配",快循环负责"对当下变化快速反应"。

3.4 优化目标:optimization_target

慢循环是否启用以及 SLA 语义由 optimization_target 配置决定,四种取值对应不同策略形态:

• sla(默认):启用回归模型估算 TTFT/ITL 与 SLA 比较,前述主路径;

• throughput:以吞吐容量为决策依据,不依赖延迟估算;

• latency(即"easy mode"):绕开回归模型,使用静态阈值——Prefill 侧以排队 token 数占上下文长度的比例为信号,Decode 侧以 KV 利用率为信号。性能模型样本不足时的安全退路;

• load:完全由用户自定义阈值驱动,最大化灵活性。

3.5 约束叠加链

决策在生效前依次通过一条约束钳制链(core/budget.py):

1. 最小副本数(min_endpoint):每侧副本不得低于下限;

2. GPU 预算带:通过 proportional_clamp_pair 将两侧副本数按 GPU 配额成比例钳制,容差步长取单副本 GPU 数的最大值,保证钳制结果始终是合法的整数副本组合;

3. 功耗预算(total_gpu_power_limit):预估总功耗不得突破限额;rollout(灰度发布)期间采取 fail-closed 策略——冻结扩容,防止放量与扩容叠加造成功耗越限。

四、性能模型:在线回归与冷启动引导

快循环的核心依赖是一组在线性能回归模型(core/perf_model/),它们必须回答两类反事实问题:"如果队列再长一些,TTFT 会是多少"、"如果批量再大一些,ITL 会是多少"。

4.1 Prefill 一维回归模型

PrefillRegressionModel 以 sum_prefill_tokens → wall_time 的一维线性回归刻画 Prefill 耗时。直接用回归截距当 TTFT 并不充分——真实 TTFT 是排队的前序请求全部完成后才轮到新请求。因此 estimate_queued_prefill_time 按引擎的 max_num_batched_tokens(单次迭代最大可处理 token 数)将排队总量切块,逐块用回归函数累加 wall time,模拟了分块调度的排队演化。KV 命中率(来自慢循环指标)作为折扣因子作用到排队 token 上,上限 0.95,防止命中率异常高时过度乐观。

4.2 Decode 二维回归模型

DecodeRegressionModel 是以 [num_decode_requests, sum_decode_kv_tokens] → wall_time 的二维回归:批量中请求数与 KV cache 总 token 数共同决定一步解码耗时。ITL 估算还需除以投机解码 accept length——一次前向生成多个 token 时,有效 ITL 被成比例摊薄。引擎容量(单副本在 ITL SLA 内可稳定承载的请求数)通过 batch size 二分搜索求得,并以 Little's law(在途请求数 = 到达率 × 停留时间)换算为 RPS 容量。

4.3 样本治理:分桶退避与物理性校验

在线模型的样本质量决定决策质量。两个机制值得注意:

• 分桶退避(bucketed retirement):FPM 样本按输入特征分桶,淘汰时整桶退避而非全局滑窗,保证低频但重要的工况(如超长序列)不会被高频工况淹没;

• 负系数拒绝:回归拟合若产生负斜率(更多 token 反而更快——物理上不可能),该拟合结果被拒绝使用,避免模型外推出荒谬决策。

4.4 冷启动引导:三级数据源优先级

新部署的集群没有任何运行时样本,性能模型不启动则快循环失明。monitoring/perf_metrics.py 中的 fetch_pre_deployment_metrics 实现了按优先级递减的三级引导:

1. worker 自测基准:调用引擎的 get_perf_metrics 端点,让引擎上线时自行跑一组合成基准,生成真实 FPM 样本;

2. AIC 插值(AI Configurator):基于 aiconfigurator_core SDK,依据模型 ID、GPU 型号与后端类型,从内置 profile 数据库插值出合成 FPM(rapid 模式);

3. 离线 profile 数据:加载 profile_results_dir 下预先采集的 NPZ/JSON profiling 数据(thorough 模式)。

三级全部失败则抛出异常拒绝启动(fail-fast);即使引导成功,运行时 FPM 仍会持续积累并最终接管回归拟合。PlannerEnginePerfModel 同时充当 aiconfigurator SDK 的包装器并支持在线修正,使冷热数据无缝衔接。

五、防震荡的缩容安全机制

扩容决策的代价只是"多花一些 GPU",而错误缩容的代价是 SLA 违约与随后的补偿性扩容,形成缩容—扩容震荡。planner 在快循环中实现了 consolidation-aware scale-down(感知合并的缩容),这是源码中最具巧思的部分。

5.1 N/(N-1) 合并重预测

缩容的本质是"把一个副本上的负载合并到其余副本"。候选动作评估时,源码以 consolidation = N/(N-1) 为缩放系数,重预测缩容后幸存副本将承受的负载水平,并执行角色化安全检查。以 Decode 为例(load_scaling.py _decode_load_decision):

• KV 容量硬检查:合并后单副本的调度中 plus 排队 KV token 总量(post_sched_kv)必须小于该引擎 max_kv_tokens。一旦越过 KV 容量边界,请求被迫排队或触发块逐出,进入性能模型无法刻画的非线性区,因此直接整体拒绝缩容;

• SLA 软检查:在合并后的 KV 负载水平下重估 ITL,若超过 itl_ms × sensitivity 则拒绝。该检查与容量检查解耦——实践中引擎常在 KV cache 远未满时延迟就已饱和。

Prefill 的合并检查则聚焦排队诱发延迟:新请求自身的前向耗时 T_own 不随副本数缩小,是不可避免的下界;只有 队列诱发部分 < (ttft_ms - T_own) × sensitivity 才允许缩容(sensitivity 即 load_scaling_down_sensitivity 配置百分比)。被拒绝时记录诊断原因 scale_down_refused_consolidation,运维可从诊断报告直接看到拒绝依据。这套机制使 2→1 这类高风险合并被系统性拦截,从根源上抑制了最常见的小规模震荡模式。

5.2 执行期保护:扩缩窗口与观测对账

除决策期检查外,还有两层运行时保护:

• scaling_in_progress Guard:副本变更从下达到 Operator 调和完成存在分钟级窗口,期间 planner 只观测不决策,避免基于过渡态连发指令;

• FPM 对账(_reconcile_fpm_worker_count):周期性比对 FPM 遥测覆盖的 worker 数与 DGD 实际副本数(含 DP rank 完整性校验)。数据投影引擎缺席意味着观测盲区,此时禁用依赖 FPM 的决策路径。

六、插件化决策流水线

前述双循环作为内置能力运行于一条插件化流水线之上(plugins/ 目录),这是 planner 面向未来扩展的关键架构投资。

6.1 五阶段流水线与合并语义

每个决策 tick 顺序经过四个阶段,EXECUTE 为最终决策投影:

• PREDICT(预测):链式执行(chain_augment)——优先级升序逐个调用,首个产出者胜(first-writer-wins),部分结果沿链传递补全,最终汇入 PipelineContext.predictions;

• PROPOSE(提议):全部插件扇出并发执行,产物经类型感知合并(type_aware_merge);

• RECONCILE(调和):对提议再次扇出过滤、重加权;

• CONSTRAIN(约束):最终钳制阶段,明确禁止 SET(直接指定副本数)类输出——只允许 AT_LEAST/AT_MOST 夹逼与 REJECT,防止约束插件越权改写提议;

• EXECUTE:不为独立阶段,而是将合并产物投影为连接器调用或跳过动作(apply / skip_no_targets / skip_short_circuit / skip_tick_timeout)。

type_aware_merge 的合并语义按类型定义:多条 SET 冲突时优先级最小者胜;AT_LEAST 取最大值作地板、AT_MOST 取最小值作天棚,与 SET 推荐值夹逼出最终目标;任一 REJECT 则整轮短路熔断,直接跳过执行。携带 final=True 标志的提议可完全覆盖其他提议(运行时对非最低优先级使用 final 做检测告警)。

6.2 内置插件

双循环被包装为三个内置插件注册进流水线(engine_adapter.py):

插件

阶段

优先级

职责

builtin_load_predict

PREDICT

0

运行流量预测器,产出 predictions

builtin_load_propose

PROPOSE

10

快循环负载微调提议

builtin_throughput_propose

PROPOSE

20

慢循环吞吐下界提议

两个 PROPOSE 插件合并的结果等价于 3.3 节的"快为主、慢为底"合成——下界语义天然由 AT_LEAST 类型表达。

6.3 节流、熔断与外部扩展

• 执行节拍:流水线基础节拍由 scale_interval_seconds 驱动;每个插件另有 execution_interval_seconds 独立周期(强制不小于基础节拍),未到期的插件以 HOLD_LAST 缓存复用上轮输出,长周期插件(如慢循环)不至于拖慢短周期决策;

• 熔断器:单插件连续失败触发 CircuitBreaker,暂停其调用并隔离故障,防止一个坏插件拖垮整条流水线;

• 外部插件:通过 gRPC Gateway 可在运行期注册外部插件(任意语言实现,FPM 采用 msgpack 跨语言编码),PipelineContext 是唯一输入通道。用户可注入自定义预测器、提议策略或约束条件,与内置逻辑同台合并——这使"动态 PD 策略"从固定算法升级为可编程框架。

七、主循环节奏与工程保障

7.1 tick 编排

planner 主循环(core/base.py 的 NativePlannerBase.run())每个 tick 顺序执行:refresh(刷新配置与集群视图)→ observe(懒拉取 Prometheus 流量、worker 计数与 FPM 快照)→ engine.tick(配置锁内驱动决策流水线)→ apply_effects(下发 ScalingDecision)→ connector.apply_scaling(连接器落地)。全流程与引擎推理路径完全异步,观测数据采集不侵入请求热路径。

7.2 可观测性与试运行

• 全链路 Prometheus 指标(插件调用耗时、合并类型计数、扩缩动作计数);

• DiagnosticsRecorder 输出 HTML 诊断报告,包含每次决策的估算 TTFT/ITL、拒绝原因(如 scale_down_refused_consolidation)、约束钳制轨迹;

• advisory(顾问)模式:只输出决策不实际执行,供新参数上线前的干跑验证。

八、局限与展望

官方设计文档(planner-design.md)坦诚列出了当前实现的边界:

1. 调整间隔与扩容延迟的错配:决策周期可到秒级,但副本冷启动(拉起、加载权重、预热)为分钟级,快循环实际"快"在检测而非生效;突发流量的第一波仍需靠过载缓冲扛过;

2. 平均值预测的天花板:负载预测器输出均值而非分布,对长尾流量形态欠拟合;

3. Local Planner 的管辖范围:当前 planner 决策限于单个 DGD 内部,跨部署的全局配比依赖 GlobalPlanner 协调,其自身仍在演进;

4. 合并假设的理想化:N/(N-1) 均摊假设缩容后负载均匀重分布,未显式建模路由器迁移成本与瞬时不均衡。

这些边界同时勾勒了演进方向:更细粒度的负载分布建模、基于分布的预测、以及与推理引擎协同的原地弹性(如 KV Cache 热迁移减少冷启动代价)。

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0