1. 摘要
Relax 当前的 GDN(Gated Delta Net)上下文并行有两条路径:head 数能被 TP × CP 整除时走 MCore 原生 headwise CP(cp2hp all-to-all,每 rank 全序列 × 1/CP heads);否则退回 _dcp_gdn_forward 的 all-gather fallback(运行时 wrapper 由 relax/backends/megatron/model.py:435-609 安装,路由条件见 model.py:491-510)——每个 GDN 层、每个 micro-batch 把完整投影序列 all-gather 到所有 CP rank(backward 对应 reduce-scatter),每 rank 重复执行整条卷积与 GDN scan,再切回本 rank 的 zig-zag 分片。fallback 语义正确,但通信量随全序列长度线性增长、GDN 计算与激活按 CP 倍数重复,并因此被 _assert_gdn_full_recompute()(model.py:339-365)强制 --recompute-granularity full;Qwen3.5 系列约每 4 层有 3 层为 GDN 层(参考 tests/utils/test_flops_counter.py 的 layer_types 配置),上述成本按层数放大。
本 RFC 提议把 NVIDIA Megatron-LM dev 分支已合入的 FLA chunkwise CP(KCP:每 rank 只持有并扫描连续时间分片,跨 rank 只交换卷积边界 token 与 (h_ext, M) 状态摘要)选择性回移到 Relax 固定的 MCore 基线上,并接入 Relax 的 per-microbatch 动态 CP、packed THD 元数据与既有回退路径。方案分两层:
- FLA/MCore 兼容层(第 3 节):FLA
0.4.1 → 0.4.2 精确升级;把上游 5139086e 中 Task 32 必需的 hunk 合入 docker/patch/ 的 MCore 补丁;不改变 Relax 任何默认路由,以 CP=1 / headwise / all_gather 三条同镜像回归门槛把"依赖升级风险"与"算法切换风险"隔离。
- Relax 接入层(第 4 节):暴露
--gdn-cp-mode={auto,chunkwise,headwise,all_gather};auto 在 v1 对现有合法配置保持原路由,非法 headwise 几何改为提前报错;chunkwise 仅显式启用;落地动态 CP group 解析和 micro-batch 级元数据复用;回退路径见第 7 节。
本 RFC 重点覆盖:构造期能力声明与运行期路由分离;FLA 升级对现有 all_gather 路径本身数值的影响;权重同步/DCS 与 checkpoint 的 TP-only 分片不变式;以及"现有 8×H200 上真实 Qwen3.5-9B 的合法拓扑不会自然命中 fallback,需要使用 forced all-gather 受控对照"的实验设计(6.2)。
2. 上游 Chunkwise CP 能力与 Relax 现状
2.1 上游如何支持 Chunkwise CP
本 RFC 区分两个上游参考:
- Megatron-LM #3282 已合入
dev 分支,对应 commit 5139086e,提供 Chunkwise CP 的完整功能实现,是本任务的必选迁移基线;
- Megatron-LM #5664 仍为 Open,提出把 THD 布局路线提前构建并存入
PackedSeqParams,解决 5139086e 在每层重复计算路线的问题。本 RFC 将其中的路线预构建最小子集作为可选性能改进,不把整个未合入 PR 当成稳定依赖。
Chunkwise CP 的基础能力由 FLA 与 MCore 两层共同完成:
- FLA 0.4.2 提供
fla.ops.cp.build_cp_context,并让 causal_conv1d、chunk_gated_delta_rule 接受 cp_context。卷积只交换每个连续分片边界的 W-1 个 token;GDN forward all-gather (h_ext, M) 状态摘要并在本地折叠,backward 交换对应状态梯度。通信量由 head 几何决定,不再随完整序列激活线性增长。
- Megatron-LM
5139086e 负责把该能力接入 MCore:新增 context_parallel_layout.py 完成 zig-zag ↔ contiguous 转换;在 GatedDeltaNet.forward 中解析 CP group、构造 cp_context 并传给 FLA kernel;在 TransformerConfig 中增加 linear_cp_mode={headwise,chunkwise} 及对应的 head 整除校验。
上游执行链路为:
rank-local hidden_states
→ in_proj
→ zigzag_to_contiguous_chunks
→ build_cp_context(cu_seqlens, group, conv1d_kernel_size)
→ causal_conv1d(..., cp_context)
→ chunk_gated_delta_rule(..., cp_context)
→ contiguous_to_zigzag_chunks
→ out_proj
这条路径进入 GDN 前临时把 zig-zag 数据换成“每 rank 一段连续时间”,计算结束后再恢复 zig-zag,因此不会改变后续模型层的数据布局。上游同时支持 headwise 和 chunkwise:headwise 要求 heads 能被 TP×CP 整除;chunkwise 只要求能被 TP 整除。
5139086e 有两个需要 Relax 额外处理的点:
linear_cp_mode 是模型构造期的静态配置,而 Relax 的 CP group 和 CP size 可以随 micro-batch 变化,不能直接用一个静态字段决定所有 forward。
- 每个 GDN 层的两次布局转换都会重复根据
cu_seqlens 计算相同的 token 搬运路线,并通过 .item() 等待 GPU 返回总 token 数;多个 GDN 层和 full recompute 会放大这部分开销。#5664 通过 prebuild_thd_cp_partition_routes 在数据准备阶段为每个 micro-batch 构建一次双向路线,后续层直接复用。
考虑到本任务目标包含长上下文性能,RFC 默认建议同时移植 #5664 的路线预构建最小子集。但该优化不影响 Chunkwise 的数值语义:若维护者希望只依赖已合入代码,可以不纳入这部分,保留 5139086e 的按需重建路线行为;其余 FLA/MCore 与 Relax 接入设计不变。
2.2 Relax 当前实现与补丁情况
依赖与本地补丁
Relax 镜像固定 Megatron-Bridge 2faedbf6 及其 MCore 85bced0a,安装 FLA 0.4.1;构建时把 Bridge 与 MCore 源码合并到 /root/Megatron-LM,再应用 docker/patch/latest/megatron.patch。当前 patch 对 GDN 有两处必须保留的兼容修复:
- Qwen3.6 的
_prepare_qkv_for_gated_delta_rule 会触发 torch.compile/Dynamo 失败,因此仅对该 helper 禁用编译并回到 eager;
get_parameter_local_cp 使用 param[tuple(slices)] 表达多维基本切片,保证 headwise CP 的卷积参数、A_log、dt_bias 沿正确维度切分。
启动与模型构造
- 参数校验阶段根据
rollout_max_context_len / max_tokens_per_gpu 推导本次任务可能使用的最大 CP,并向上取到 2 的幂;该值写入 args.context_parallel_size,作为静态最大 CP。
mpu.initialize_model_parallel(..., dynamic_context_parallel=True) 在初始化阶段创建静态 CP 组及可复用的动态 CP 子组;forward 内不会临时创建 process group。
- 当前 MCore 没有
linear_cp_mode,TransformerConfig.__post_init__ 固定按 headwise 要求 heads 被 TP×静态CP 整除。Relax 在模型构造前安装 _relax_gdn_cp_config_assert:当 heads 只满足 TP 整除时,临时把 key/value head 数乘以 CP,使原生校验通过,随后恢复真实值。该操作只绕过构造检查。
- 模型创建后,Relax 通过
_patch_gdn_for_dynamic_cp 替换 GatedDeltaNet.forward,安装运行时路由 wrapper。
每个 micro-batch 的执行流程
data.py 根据该 micro-batch 的最长样本和每 GPU token 预算计算 runtime_CP(1/2/4…),选择初始化阶段已创建的对应 CP 子组,并得到当前 rank 在组内的 cp_rank。
- 数据随后进入两条 THD 准备路径之一:
- 标准 pre-split THD:
data.py 先按 cp_rank 对每条样本做 zig-zag 切分,再拼成 rank-local THD tensor,构造 batch["packed_seq_params"](data.py:409-442);
- Bridge/VLM unsplit THD:Qwen3-VL 及共用该 Bridge 架构的文本 Qwen3.5/Qwen3.6 先接收未切分的
input_ids 和 attention mask,由 Bridge 在 embedding 后完成 CP/SP 切分,使用 batch["vlm_packed_seq_params"](data.py:352-382、model.py:1039-1042)。
- 标准路径把
cp_group、local_cp_size 和全局边界写入最终 PackedSeqParams;VLM 路径先把动态组和初始边界写入 vlm_packed_seq_params,Bridge repack 后的最终对象和边界仍需在接入层重新确认。
- GDN wrapper 从该对象解析
runtime_CP/group/rank:CP=1 或非 THD 走原生路径;THD CP>1 且 heads 被 TP×runtime_CP 整除时调用 MCore headwise;否则进入 Relax all-gather fallback。
- all-gather fallback 先在本地执行
in_proj,再收集所有 CP rank 的投影并按 zig-zag 规则重组完整序列;每个 rank 重复执行完整卷积和 GDN scan,最后切回本 rank 的 zig-zag 输出并执行 out_proj。该路径训练时要求 full recompute。
fallback 首次使用 GPU cu_seqlens 时会转成 CPU list,并以 _gdn_cu_seqlens_cpu 缓存在当前 PackedSeqParams(model.py:524-531);同一 micro-batch 的后续 GDN 层直接复用,避免每层重复 .tolist() 和 D2H。该缓存只服务旧 fallback 的序列重组/切片,不包含 Chunkwise 的布局 route 或 cp_context。
因此上游与 Relax 的核心异同是:
| 维度 |
上游 |
Relax 当前 |
接入要求 |
| FLA |
0.4.2,提供 cp_context |
0.4.1 |
精确升级到 0.4.2 |
| CP 模式 |
静态 headwise / chunkwise |
运行时 headwise / all_gather |
保留现有默认路由,新增显式 chunkwise |
| 动态 CP |
resolve_cp_group 优先读取 PackedSeqParams.cp_group |
data.py 每 micro-batch 选择 group/size,GDN wrapper 自行解析 |
收敛成单一解析逻辑,不在 forward 建组 |
| zig-zag 与 contiguous THD转换路由 |
5139086e 每次交换重建;#5664(Open)提出在数据准备阶段预建 |
尚无 chunkwise 路由 |
默认纳入 #5664 的最小路线缓存子集;维护者不同意时可退回按需重建 |
| 数据布局 |
zigzag_to_contiguous_chunks 约定输入为 zig-zag |
常规路径是 zig-zag;--allgather-cp 已是连续切分 |
拒绝 chunkwise 与 --allgather-cp 同开 |
2.3 迁移结论
5139086e 比 Relax 的 MCore 基线领先约 597 个提交,且 Relax 使用“Bridge + MCore 合并树 + 自有 patch”的装配方式,不能直接 cherry-pick。迁移应采用选择性 backport:
- FLA 精确升级为 0.4.2,提供
build_cp_context 以及支持 cp_context 的卷积/GDN kernel;
- 选择性回移
context_parallel_layout、GDN chunkwise forward、linear_cp_mode,并补齐旧基线缺少的 resolve_cp_group 等前置能力;
- 合并上游 hunk 时保留第 2.2 节列出的两处 Relax GDN 兼容修复,避免 backport 覆盖现有可运行路径;
- 为 GDN forward 增加单次调用级的 mode override,使同一个模型可按 micro-batch 选择模式而不改写共享 config;默认同时回移 #5664 的最小 THD 路线预构建接口,该项可按维护者意见独立移除;
- 默认
auto 对现有合法配置保持原行为:CP=1/非 THD 走原生路径,THD CP>1 时可整除走 headwise、否则走 all_gather;非 THD 的非法 headwise 几何改为提前报错。chunkwise 仅显式启用,现有 all-gather fallback、TP-only 权重分片和 checkpoint 行为全部保留。
2.4 备选方案与取舍
- 整体升级 Megatron-Bridge/MCore:可直接获得较新的接口,但会同时改变 Attention、并行初始化、checkpoint、optimizer 和模型转换,难以隔离 Task 32 的收益与回归,因此不采用。
- 长期在 Relax monkey patch 中实现 Chunkwise:便于 PoC,但依赖 MCore 内部函数和参数布局,升级脆弱;生产 GDN conv/scan 与布局消费应回到 pinned MCore,Relax 只保留策略和 per-microbatch 元数据。
- 整体迁移 #5664:该 PR 尚未合入且覆盖 GPT/Hybrid/Attention/MTP/Qwen-VL,超出任务范围;只把 THD route 预构建最小子集作为可选 hunk。
下一节据此给出 FLA 升级、MCore patch 生成和逐文件 backport 方案。
3. FLA/MCore 兼容方案
3.1 FLA 升级与验证
Relax 当前固定 FLA 0.4.1,但该版本没有 Chunkwise CP 所需的 fla.ops.cp,卷积和 GDN kernel 也不接受 cp_context。因此 docker/Dockerfile:41 必须精确升级为 flash-linear-attention==0.4.2;
镜像构建后先做两类检查:
- 能力检查:确认
build_cp_context 可以导入,并确认 causal_conv1d、chunk_gated_delta_rule 已支持 cp_context 参数;不满足则直接判定镜像构建失败。
- 现有路径回归:FLA 升级不仅影响新增的 chunkwise,Relax 当前的 CP=1、headwise 和 all-gather 也在调用 FLA 的卷积/GDN kernel(
model.py:560-598)。因此候选镜像必须分别与旧镜像比较这三条路径的 forward 和 backward;任何一条数值回归,都不能把问题归因于 chunkwise 接入,而应先阻断 FLA/MCore 兼容层合入(具体门槛见 3.4)。
报告记录最终安装的 FLA 版本、镜像 digest 和对应 tag commit ca910f88,避免后续重建环境时出现版本漂移。
3.2 Patch 方案
Relax 不直接在仓库中维护一份完整 MCore 源码。镜像构建时先组合固定版本的 Megatron-Bridge 2faedbf6 与 MCore 85bced0a,再把 docker/patch/latest/megatron.patch 应用到组合后的源码树(docker/Dockerfile:130-139)。因此 Chunkwise CP 的 MCore 改动也必须进入这份 patch。
上游 5139086e 比 Relax 的 MCore 基线新约 597 个提交,且 gated_delta_net.py、transformer_config.py 与 Relax 现有 patch 修改了相同区域,无法直接 cherry-pick。方案是在当前组合源码树上选择性移植第 3.3 节列出的能力,并重新生成一份包含“现有 Relax 修复 + Chunkwise CP backport”的完整 MCore patch。新的带日期 patch 保存到 docker/patch/megatron/,docker/patch/latest/megatron.patch 同步指向同一内容。
镜像构建必须验证 patch 能干净应用、无冲突标记,并能导入新增的布局模块和 GDN 模块。提交说明同时提供文件/hunk 级清单:说明哪些上游改动被采用、哪些被排除,以及排除原因,方便维护者确认没有夹带无关的 MCore 升级。
3.3 逐文件 backport 清单
这是 MCore patch 的核心范围:
| 目标文件 |
必须移植的能力 |
Relax 适配 |
megatron/core/context_parallel_layout |
必选:5139086e 的 zig-zag ↔ contiguous 转换、THD rank 索引与 all-to-all 路由;可选但建议纳入:从 #5664 最小回移 route 的 build/get/prebuild/decode helper |
采用可选优化时,布局转换优先读取预构建 route,未预构建时回退到 5139086e 的按需构建 |
megatron/core/ssm/gated_delta_net.py |
解析运行时 CP group;进入/退出时转换布局;构造 cp_context;把它传给卷积与 GDN kernel |
保留第 2.2 节两处 Relax 修复;增加 call-level mode override;仅在采用 #5664 可选优化时接入预构建 route;不移植 pre-GDR fusion 等后续优化 |
megatron/core/transformer/transformer_config.py |
linear_cp_mode 字段,以及 headwise 按 TP×CP、chunkwise 按 TP 校验 heads 的规则 |
不采用上游默认 "chunkwise";由 Relax 在构造期声明可用能力,实际模式仍由运行时分发器决定 |
megatron/core/packed_seq_params.py |
必选:resolve_cp_group;可选但建议纳入:#5664 的 cp_partition_mode、zig-zag→contiguous / contiguous→zig-zag route tensor 字段 |
local_cp_size/cp_group 已存在于 85bced0a,无需重复 backport;维护者不接受未合入优化时,只补统一 group 解析 |
megatron/core/extensions/transformer_engine.py |
修正混合模型中普通 Attention 层的 THD 输出长度:输入 tensor 含对齐 padding 时,TE 必须使用 padded cu_seqlens 分配同样长度的输出 |
当前 Relax 基线没有 pad_between_seqs 字段;只有本次 backport 同时采用该 padding 表示、且测试证明缺少此修复会导致输入/输出长度不一致时才纳入 |
上游通过 pyproject.toml/uv.lock 声明 FLA 依赖,但 Relax 的 MCore 是在 Docker 中组装而不是通过这些文件安装,因此同一依赖改动应落在 docker/Dockerfile,不复制上游的包管理文件。上游布局与 GDN 测试会改编到 tests/backends/megatron/;它们只验证被移植的基础能力,Relax 仍需第 5 节的自建测试覆盖动态 CP、VLM/unsplit 路径和现有 all-gather fallback。
#5664 改动范围很大,本任务不整体迁移。默认只采用与 THD 路由复用直接相关的最小子集:PackedSeqParams 的布局/route 字段,route 的构建与读取 helper,以及布局转换消费预构建 route 的接口;不迁移其 GPT/Hybrid 全局布局规划、Attention/MTP/Qwen-VL 改造等其他内容。兼容层负责提供这套 MCore 接口,Relax 接入层只负责在 data.py 构造 micro-batch 时调用它。由于该 PR 尚未合入,实施时固定并记录实际采用的 head commit;若维护者不接受未合入代码,则删除这组可选 hunk 和 data.py 预构建调用,继续使用 5139086e 的按需构建逻辑。
3.4 兼容层完成标准
本阶段只判断“FLA/MCore 能力是否正确迁移”,不验证 Relax 的动态 CP 路由和完整训练矩阵;后者统一放在第 5 节。完成标准为:
- 新 patch 能干净应用,
build_cp_context、布局转换模块和修改后的 GDN 可以正常导入;
- CP=1 与静态 headwise 的 forward/backward 相对旧镜像无数值回归;现有 all-gather fallback 也需回归,因为它同样调用本次升级的 FLA kernel;
- 最小 CP=2 Chunkwise 用例能够调用真实 FLA kernel,且 forward/backward 与 CP=1 参考结果满足容差,证明迁移的能力本身可用;
- GDN 的
state_dict / sharded_state_dict 参数键与分片维度保持不变,证明迁移没有改变 TP-only 参数和 checkpoint 语义。
该阶段不修改默认 recipe,也不让 auto 选择 chunkwise。
4. Relax 接入方案
4.0 变更范围
| 文件 |
修改内容 |
目的 |
relax/utils/arguments.py |
新增 --gdn-cp-mode={auto,chunkwise,headwise,all_gather} 及参数冲突校验,默认 auto |
给用户显式选择和回退入口,同时保持旧命令行为不变 |
relax/backends/megatron/model_provider.py |
仅对 GDN 模型在 provider.finalize() 前设置 MCore 的 linear_cp_mode 能力声明 |
让 TransformerConfig 用正确规则完成构造期 head 整除校验;普通 Attention 模型配置不变 |
relax/backends/megatron/model.py |
把现有 GDN wrapper 重构为 dispatcher;在 Bridge/VLM 完成最终 repack 后验证实际 PackedSeqParams 的 padded 边界并准备 route |
负责 per-microbatch 路由、call-level override,并保证 VLM 不会复用 embedding 前已经失效的边界/route |
relax/backends/megatron/data.py |
标准 THD 在 runtime CP 已确定后保留 CPU padded 边界,并在采用 #5664 时预构建 route;VLM/unsplit 只传递原始 host 长度,最终边界由模型入口在 repack 后确认 |
避免 GDN 层内新增 D2H,同时区分“可在 data.py 最终确定”的标准路径与“会被 Bridge 改写”的 VLM 路径 |
tests/backends/megatron/ |
增加第 5 节的布局、路由、真实 kernel、动态 CP、VLM 和 checkpoint 测试 |
同时验证上游能力与 Relax 特有接入,而不是只运行上游单测 |
scripts/training/sft/run-qwen3.5-9B-math-dynamic-cp-8xgpu.sh |
暴露 GDN_CP_MODE、SEED、RUN_ID,并让不同实验使用独立日志/checkpoint 路径 |
作为真实训练入口运行四种模式和 before/after 对照,避免实验互相续跑或覆盖 |
scripts/benchmarks/ |
增加第 6 节所需的 step time、有效 tokens/s、峰值显存、GPU 利用率与通信时间采集 |
生成可复现的性能证据,而不是只报告单次日志中的平均速度 |
4.1 模式与参数
上游只提供模型级的静态 linear_cp_mode,但 Relax 的实际 CP size/group 会随 micro-batch 变化,而且还需要保留自有 all-gather fallback。一个共享静态字段无法同时表达这些运行时选择;直接在 forward 中改写它又会让交错 micro-batch 或 recompute 读到错误状态。因此本方案把“模型构造期采用哪套校验规则”和“本次 forward 实际走哪条路径”分开:前者只作为能力声明,后者由 Relax dispatcher 通过 call-level override 决定。
新增 --gdn-cp-mode {auto,chunkwise,headwise,all_gather},默认 auto。四个模式的用途是:
| 模式 |
设计目的 |
运行行为 |
auto |
保持现有合法 recipe 向后兼容 |
每个 micro-batch 继续使用当前规则:CP=1/非 THD 走原生路径;THD CP>1 且 heads 可被 TP×runtime_CP 整除时走 headwise,否则走 all-gather。不自动选择 chunkwise;非 THD 的非法 headwise 几何提前报错 |
chunkwise |
显式试用和评测新路径 |
CP>1 时通过 call-level override 调用 FLA Chunkwise;不支持 deterministic 或 --allgather-cp,非法组合直接报错 |
headwise |
回归对照和快速回退 |
强制调用 MCore headwise;当前 runtime CP 下 heads 不能整除时直接报错,不改走其他路径 |
all_gather |
保留旧 fallback,作为不可整除几何的回退和正确性参照 |
CP>1 时仅支持 packed THD,强制调用现有 Relax all-gather 实现;训练继续要求 full recompute。CP=1 仍走普通非 CP 路径 |
构造期校验与运行时路由分离
MCore 回移 linear_cp_mode 后,以该字段替代当前 _relax_gdn_cp_config_assert 的临时 head 数改写。构造期只需选择能正确放行当前模型的校验规则:显式 headwise 或静态几何可整除的 auto 声明为 headwise;其余模式声明为 chunkwise,采用 TP-only 校验。后者只用于让 all-gather/Chunkwise 可用的不可整除模型完成构造。
实际执行路径仍由 dispatcher 按用户模式和每个 micro-batch 的 runtime_CP 通过 call-level override 决定;auto 继续保持“可整除走 headwise,否则走 all-gather”,不自动进入 Chunkwise。dispatcher 必须显式传入本次模式,不能依赖构造期默认值或在 forward 中改写共享 config;显式模式不做静默 fallback。
4.2 动态 CP 接入流程
动态 CP 要保证同一个 micro-batch 的数据切分、布局路线和所有 collective 使用同一 CP size/group。接入流程如下:
- 初始化通信组:配置中的
context_parallel_size 表示最大 CP。初始化阶段创建最大组及 size 1/2/4… 的动态子组;forward 不创建新 group。
- 选择 runtime CP:
data.py 根据最长样本和每 GPU token 预算选择子组,把 cp_group 和 local_cp_size 写入 PackedSeqParams。二者必须同时存在且 local_cp_size == cp_group.size()。
- 生成最终 padded 边界:每条序列必须满足
padded_length % (2×runtime_CP) == 0。标准 THD 在 data.py 中按 runtime CP padding/slice;VLM/unsplit 必须等 Bridge 在 embedding 后完成最终 repack,再使用最终 cu_seqlens_q_padded(没有时才用 cu_seqlens_q)检查,不能复用 repack 前的临时边界。
- 准备元数据:保留最终边界的 CPU 形式并附到最终
PackedSeqParams,供 FLA build_cp_context 使用,避免每层 D2H。若采用 #5664,同时预构建双向 route;不采用时由 5139086e 在布局转换处按需构建。
- 统一消费:dispatcher 通过
resolve_cp_group 从该对象解析 group/size/rank;MCore 布局转换和 FLA kernel 均使用同一结果。PackedSeqParams 随该 micro-batch 经过全部 GDN 层和 recompute,因此边界和可选 route 可复用。
自定义数据路径未提供 CPU 边界时,首个 GDN 层允许做一次转换并打印 warning。最终长度不满足 2×runtime_CP 时,在第一条布局 collective 前报错;动态 CP 正确性 gate 通过前不进行性能归因。
4.3 dispatcher 重构与旧路径收敛
_patch_gdn_for_dynamic_cp 重构为显式分发器:
def _gdn_forward_dispatch(self, hidden_states, attention_mask,
inference_context=None, packed_seq_params=None, *args, **kwargs):
cp_size, cp_group, cp_rank = _resolve_and_validate_cp_group(self, packed_seq_params)
requested_mode = get_args().gdn_cp_mode
is_thd = packed_seq_params is not None and packed_seq_params.qkv_format == "thd"
# CP=1 不需要 CP 通信。
if cp_size == 1:
return _call_orig_with_dynamic_cp(...)
if requested_mode == "auto":
if not is_thd:
mode = "headwise" # 保持当前原生路径;非法 head 几何由下方校验拒绝
else:
mode = "headwise" if self.num_key_heads % (self.tp_size * cp_size) == 0 else "all_gather"
else:
mode = requested_mode
if mode == "headwise":
_validate_headwise_divisibility(self, cp_size)
return _call_orig_with_dynamic_cp(..., linear_cp_mode_override="headwise")
if mode == "chunkwise":
# patched MCore 从 packed_seq_params 读取预构建路线
return _call_orig_with_dynamic_cp(..., linear_cp_mode_override="chunkwise")
return _all_gather_gdn_forward(...) # 现 _dcp_gdn_forward 主体,原样保留
auto 解析出 headwise 后也必须显式传入 linear_cp_mode_override="headwise"。否则,当构造期为放宽校验而设置了 linear_cp_mode=chunkwise、但某个较小 runtime_CP 又满足 headwise 整除时,直接调用原始 forward 会误走 Chunkwise。各分支的实际执行路径由第 5.2 节测试观测验证。
现有代码按以下方式处理:
- all-gather fallback 的实现保持不变:
_AllGatherFullSequence 负责带 autograd 的全序列收集,gdn_reassemble_full 恢复顺序,gdn_cp_slice 切回本 rank 输出;它继续作为显式 all_gather 模式和 Chunkwise 的 CP>1 正确性参照;
_assert_gdn_full_recompute 只在 all-gather 分支执行,因为只有该路径在每个 CP rank 保留完整序列激活;headwise/Chunkwise 不应继承这项限制;
- 删除
_relax_gdn_cp_config_assert 的临时 head 数改写,由第 4.1 节的正式 linear_cp_mode 构造校验取代;
- 用统一的
_resolve_and_validate_cp_group 取代当前 _resolve_gdn_cp:内部调用 backport 的 resolve_cp_group 选择动态或静态组,再派生并校验 size/rank,避免 Relax 与 MCore 各自维护一套 group 解析逻辑。
5. 正确性验证计划
参照基准为 CP=1 全序列 GDN;CP>1 另外使用现有 all-gather 路径交叉核对。数值门槛沿用对应上游测试:
| 层级 |
比较对象 |
门槛 |
| FLA GDN kernel |
output、dq/dk/dv/dg/dbeta |
归一化 RMS 误差 < 2e-3(FLA test_cp_gdn.py) |
| FLA causal conv |
output、dx/dw/dbias |
归一化 RMS 误差 < 1e-3(FLA test_cp_conv.py) |
| MCore GDN,CP=1 |
候选镜像 vs 旧镜像 |
atol=2e-4, rtol=2e-3,cosine similarity ≥ 0.99999 |
| MCore GDN,CP>1 |
Chunkwise/headwise/all-gather vs CP=1 |
atol=2e-3, rtol=1e-2,cosine similarity ≥ 0.9999 |
所有用例同时要求有效 token 数和 mask 精确相等,且输出、梯度无 NaN/Inf。
上述阈值以 FLA 0.4.2 和 Megatron-LM 5139086e 的现有测试为起点;若目标 bf16 几何需要调整,必须在正式 before/after 运行前根据固定输入的误差分布一次性冻结并记录,不能在看到候选结果后放宽。
5.1 布局与元数据单元测试
- CP∈{1,2,4,8},覆盖单样本、多条不等长样本、每样本 padding 和独立 padding 尾;每个 rank 的 zig-zag→contiguous→zig-zag 必须逐 token 往返恒等;
- 断言 MCore
get_thd_context_parallel_rank_indices(..., "zigzag") 与 Relax slice_with_cp / gdn_reassemble_full 的 token 所有权和顺序一致,避免两边对 zig-zag 定义不同;
- 若采用 #5664:预构建 route 与按需 route 结果一致;同一
PackedSeqParams 跨层/recompute 复用,换 micro-batch 或 CP 子组后使用新 route;
- 动态 CP 的标准 THD 与 VLM/unsplit 最终
PackedSeqParams 均验证 diff(route_cu) % (2×runtime_CP) == 0;测试包含 embedding/repack 前后边界变化,确保 route 绑定最终对象;
- 覆盖可整除/不可整除几何的
TransformerConfig 构造,确认第 4.1 节的构造校验能够替代 _relax_gdn_cp_config_assert。
5.2 真实 kernel 分布式测试(CUDA/NCCL)
真实 CUDA/NCCL 测试分两层:
- 直接调用带
cp_context 的 FLA causal_conv1d 和 chunk_gated_delta_rule,与无 CP kernel 比较输出和各输入梯度;序列长度覆盖 63/64/65 等 kernel chunk 边界,以及卷积宽度 W 附近的短分片。
- 调用完整 MCore
GatedDeltaNet,覆盖静态 SBHD(CP>1 时 batch=1)以及 packed THD 的单样本、多不等长样本、padding 尾;比较模块输入梯度和 conv/in_proj/out_proj/A_log/dt_bias 参数梯度。
几何覆盖 GQA(value heads 为 key heads 的整数倍)、key/value head dim 不同、TP/SP 开关和不可整除合成几何(如 4 key heads @ TP2/CP4)。四种用户模式必须通过 branch hook/counter 断言实际执行分支,不能只凭数值相近判断路由正确。另测试 chunkwise + --allgather-cp、deterministic、动态 SBHD、initial_state / output_final_state=True 和非法 head 几何均按设计报错。
5.3 并行 / 数据矩阵
| TP |
CP |
SP |
输入 |
说明 |
| 1 |
1 |
off |
SBHD + THD |
参照基准 |
| 1 |
2 |
off |
SBHD(b=1);THD 单/多样本 |
最小 CP |
| 2 |
2 |
on/off |
THD |
TP×CP 交叉 |
| 1 |
4 |
off |
THD |
中等 CP |
| 2 |
4 |
on |
THD |
主拓扑(单节点 8×H200) |
| 1 |
8 |
off |
THD |
布局 / 长上下文压力 |
动态 CP:静态 CP=4 组内 cp∈{1,2,4} 混合 micro-batch,断言逐 mb 使用正确子组、无跨子组 collective;四种模式在该矩阵下的路由分布可复现。
5.4 checkpoint 与 recompute
- 同拓扑 save/resume:优化器与调度器状态、续跑首 step loss 连续;
- 权重 CP=1 保存 → CP>1 加载、CP>1 保存 → CP=1 加载;以
sharded_state_dict 键集合和分片维度对旧镜像逐项相等,验证 GDN 参数仍为 TP-only;
- 跨 CP 优化器状态恢复仅在现有分布式 checkpoint 已支持的范围内验证,不新造通用 resharder,超范围如实记录为限制;
- full recompute recipe 全程通过;chunkwise 下 recompute 开/关 parity(显存允许时)。
真实连续性使用同一 Qwen3.5-9B 配置做两条对照:A 连续训练 30 step;B 训练 20 step 后保存,在新进程恢复并继续 10 step。比较 step 21–30 的数据顺序、学习率、loss、grad norm 和最终参数;该用例通过后才满足摘要中的生产 gate。
5.5 端到端连续性
主 recipe 短训(≥30 step,CP=4):auto 与显式 chunkwise 的 loss 曲线、grad norm、有效 token 数三线对照;有效 token 数逐 step 精确相等。
另选一个不含 GDN 的现有 CP recipe 做冒烟回归:默认 auto 下不得进入任何 GDN mode/route 逻辑或预构建 THD route,确认普通 Attention CP 路径与当前版本一致。
6. 性能与质量计划
6.1 冻结环境与运行纪律
- 模型/数据:Qwen3.5-9B 与 OpenMathReasoning-mini 使用固定 revision/文件 hash,具体值在首个基线运行前记录;
- 硬件:使用一台单节点 8×NVIDIA H200 完成全部正式对照;不跨节点、不混用 GPU SKU 或互联拓扑。首个基线运行前记录每卡显存容量、驱动/CUDA 版本、
nvidia-smi topo -m、CPU/RAM 和镜像信息;
- 运行:每配置 220 step(前 20 warmup 剔除、后 200 计量);paired seeds 1234/1235/1236,同一重复内所有模式用同 seed 同数据序;每配置×每重复独立
RUN_ID 命名空间;测量期禁 resume(resume 测试独立目录、不计入性能);显存峰值统计 warmup 后 reset,启动期峰值单列;
- 每种模式使用独立 Ray job/新进程,避免 compile cache、CUDA allocator 和残留模型状态污染对照;三组 paired run 轮换执行顺序(正序、逆序、交错),降低机器温度和时间漂移带来的系统偏差;
- 全部失败/OOM 运行保留并报告;每次运行记录 Relax commit、镜像 digest、MCore patch digest、FLA 版本和 recipe。
6.2 实验设计
E1(主实验:四方对照)。使用真实 Qwen3.5-9B、TP2/CP4/SP on 和同一份 SFT recipe:
- 旧镜像默认模式(当前该几何走 headwise),作为完整 before 基线;
- 候选镜像 forced
headwise,隔离 FLA 升级和 MCore forward 重构的依赖影响;
- 候选镜像 forced
all_gather;
- 候选镜像 forced
chunkwise。
比较 ④ vs ③ 衡量“用 Chunkwise 替换 fallback”的收益;④ vs ② 判断相对现有高效 headwise 是否非劣;② vs ① 单独量化依赖升级影响;④ vs ① 给出完整 before/after 结果。③ 与④首先使用相同 full-recompute 配置以保证公平,随后可增加④关闭 full recompute 的容量/性能实验。all-gather OOM 时作为容量结果报告,不虚构吞吐比。
Qwen3.5-9B 在现有 8×H200 资源的合法拓扑下会自然走 headwise,因此 forced all-gather 是受控算法对照,不代表默认线上路由。若后续能获得 32 卡,追加 TP2/CP16 的真实不可整除实验;否则用第 5 节不可整除合成几何完成正确性与 kernel 级性能验证,不把随机初始化模型作为 200-step 质量主实验。
E2(动态 CP)。先以真实模型连续 micro-batch 覆盖 cp∈{1,2,4},要求最终 padded 边界全部满足 2×runtime_CP 整除且 forward/backward 通过;该正确性 gate 未通过时只报告 blocker,不进行动态 CP 性能归因。通过后再运行 dynamic-CP recipe,对比候选镜像 auto 与显式 chunkwise,并报告逐 micro-batch CP 大小分布。
E3(单层 shape crossover)。在同一 8×H200 TP2/CP4+SP 环境中,对单层 GDN 的 8K、16K、32K 序列分别比较 headwise 与 Chunkwise,使用独立进程和轮换顺序。该实验不替代完整模型结果,只用于识别布局转换与状态摘要通信随 shape 变化的性能拐点。
6.3 指标与统计口径
- 有效吞吐(主指标):
Σ(计量步有效非 padding token) / Σ(计量步各 rank 最大同步墙钟);
- step time:mean / median / p95 / std;总 tokens/s 另列;
- GPU 利用率:
nvidia-smi --query-gpu=timestamp,index,utilization.gpu,memory.used --format=csv,noheader,nounits --loop=1 1 Hz 逐卡原始样本留档;
- 显存:warmup 后 reset 的
torch.cuda.max_memory_allocated / max_memory_reserved,8 张 H200 取最大;
- 通信:抽样 step 的 profiler trace 中 layout all-to-all 与
(h_ext, M) all-gather 用时;若采用路线预构建优化,同时确认 GDN 层内没有 route metadata 引发的重复 D2H/host wait;
- 训练侧:逐 step loss、grad norm、精确有效 token 数。
6.4 判定阈值(首个基线运行前冻结;开跑后如需变更则全配置重跑)
- fallback 替换通过:④/③ 的有效吞吐比几何平均 ≥ 1.10 且逐对 > 1.00;或 ④/③ 的峰值 allocated 显存比 ≤ 0.90 且逐对 < 1.00、同时吞吐比 ≥ 0.95;
- 如果③ all-gather 在相同工作负载下 OOM 而④ Chunkwise 完成,则记为 fallback 容量 gate 通过,同时保留 OOM 证据,不计算吞吐比;
- headwise 非回归:④/② 吞吐比 ≥ 0.95,且④/②峰值 allocated 显存比 ≤ 1.05;该几何的 headwise 本就没有重复计算,不要求 Chunkwise 必须更快;
- 依赖非回归:②/①吞吐比 ≥ 0.95,且②/①峰值 allocated 显存比 ≤ 1.05;超出时作为 FLA/MCore 依赖变化单独分析;
- 有效 token 数逐配置精确相等;全部逐对比值公开发布,不只报均值。
6.5 质量非回归
- 训练 loss:计量段(step 21–220)按有效 token 加权平均,④相对①/②/③逐 seed 差值 ≤ 0.01 nats/token;对齐的逐 step 曲线与 grad norm 随报告发布;
- held-out 质量:从同源数据固定 512 行 held-out(训练集显式剔除并记录切分 hash),确保每个 220-step 运行保存最终 checkpoint,再离线评 token loss(
Σ label_nll / Σ valid_label_tokens);④相对①/②/③逐 seed 差值 ≤ 0.01 nats/token。
6.6 报告与证据
最终报告包含:全部固定修订(Relax commit、镜像 digest、patch digest、FLA 版本、模型/数据修订)、完整命令与并行拓扑、第 5 节全部正确性结果与误差分布、6.2 全部运行(含失败/OOM)、逐对比值与对齐曲线、已知限制与第 7 节回退命令。
7. 回退路径
- 先回到旧执行路径:recipe 移除显式
--gdn-cp-mode chunkwise,恢复默认 auto。新版本的 auto 与当前 Relax 行为一致:可整除时走 headwise,否则走 all-gather。排查时也可显式指定 headwise;不可整除几何则指定 all_gather,并保留现有 full recompute 配置。
- 再回到旧兼容环境:如果 CP=1/headwise 也出现回归,说明问题可能来自 FLA 0.4.2 或 MCore backport,而不只是 chunkwise 路由。此时恢复任务开始前记录的旧 Relax commit 和对应训练镜像(包含旧 MCore patch 与 FLA 0.4.1),不要混用未经验证的新源码与旧镜像。
- 最后回滚代码:确认需要永久撤销时,先排空运行中的训练任务,再回滚 Relax 接入改动;若兼容层本身有问题,再一并回滚 MCore patch 与 FLA 升级。回退后运行 CP=1 和 headwise 冒烟测试。
正确性与性能报告记录新旧 Relax commit、镜像 digest、MCore patch digest、FLA 版本及对应 recipe,保证上述回退组合可以复现。
8. 风险与缓解
| 风险 |
缓解 |
| FLA 0.4.2 升级改变现有三条路径的数值/性能 |
3.4 同镜像三路径回归门槛(含 all_gather) |
| 选择性 backport 偏离上游或遗漏前置能力 |
固定上游 commit,提供 hunk 级 include/exclude 清单,移植对应上游测试 |
| #5664 尚未合入且接口继续变化 |
只采用 route 预构建最小子集并固定实际 head commit;该组 hunk 可独立删除,退回 5139086e 的按需构建 |
构造期 linear_cp_mode 与运行时实际模式不一致 |
CP>1 时 dispatcher 始终显式传入 call-level override,并以 branch hook 测试四种模式 |
| 动态 CP group 解析不一致导致 collective 挂死 |
所有模块使用同一 resolve_cp_group 结果;group 只在初始化阶段创建;显式模式不静默回退 |
动态 CP 的最终 packed 长度不满足 2×runtime_CP,或 route 绑定了 VLM repack 前的旧边界 |
在最终 PackedSeqParams 上选择 padded 边界并做整除校验;真实 cp∈{1,2,4} gate 通过后才做动态性能实验 |
| packed 边界或布局路线错误导致跨样本状态污染 |
多样本、padding 边界和 VLM/unsplit 路径均与 CP=1 比较输出及梯度 |
| Chunkwise 的状态通信/布局转换开销抵消收益 |
进行第 6 节四方 200-step 对照;未达阈值则保持 auto 现状并使用 headwise/all-gather 回退 |
9. 请求维护者决策
- 是否接受以
5139086e 为功能基线,选择性 backport 到 Relax 固定的 MCore 85bced0a,而不整体升级 Megatron-Bridge/MCore?
- 是否接受第 4.1 节的构造期校验与运行时路由分离:
linear_cp_mode 只用于模型构造,CP>1 的实际模式始终由 dispatcher 通过 call-level override 显式传入?
- 是否接受
--gdn-cp-mode={auto,chunkwise,headwise,all_gather} 的命名和语义,其中 auto 保持当前 headwise/all-gather 路由,不自动启用 Chunkwise?
- 现有资源是 8×H200,Qwen3.5-9B 在 TP2/CP4 的
auto 模式下会选择 headwise,不会选择 all-gather。计划在同一配置下分别强制运行 all_gather 和 chunkwise,用来比较 fallback 替换收益,同时保留 headwise 作为当前路径的对照。这个实验能否作为主要性能证据?如果要求使用 auto 模式本身就会选择 all-gather 的真实 Qwen3.5-9B 配置,则需要 32 卡 TP2/CP16;8 卡的不可整除合成配置只用于正确性和单层性能测试。
- 是否接受默认回移尚未合入的 #5664 中 THD route 预构建最小子集?若不接受,将删除该组可选 hunk 与
data.py 调用,保留 5139086e 的按需路线构建。
1. 摘要
Relax 当前的 GDN(Gated Delta Net)上下文并行有两条路径:head 数能被
TP × CP整除时走 MCore 原生 headwise CP(cp2hp all-to-all,每 rank 全序列 ×1/CPheads);否则退回_dcp_gdn_forward的 all-gather fallback(运行时 wrapper 由relax/backends/megatron/model.py:435-609安装,路由条件见model.py:491-510)——每个 GDN 层、每个 micro-batch 把完整投影序列 all-gather 到所有 CP rank(backward 对应 reduce-scatter),每 rank 重复执行整条卷积与 GDN scan,再切回本 rank 的 zig-zag 分片。fallback 语义正确,但通信量随全序列长度线性增长、GDN 计算与激活按 CP 倍数重复,并因此被_assert_gdn_full_recompute()(model.py:339-365)强制--recompute-granularity full;Qwen3.5 系列约每 4 层有 3 层为 GDN 层(参考tests/utils/test_flops_counter.py的layer_types配置),上述成本按层数放大。本 RFC 提议把 NVIDIA Megatron-LM
dev分支已合入的 FLA chunkwise CP(KCP:每 rank 只持有并扫描连续时间分片,跨 rank 只交换卷积边界 token 与(h_ext, M)状态摘要)选择性回移到 Relax 固定的 MCore 基线上,并接入 Relax 的 per-microbatch 动态 CP、packed THD 元数据与既有回退路径。方案分两层:0.4.1 → 0.4.2精确升级;把上游5139086e中 Task 32 必需的 hunk 合入docker/patch/的 MCore 补丁;不改变 Relax 任何默认路由,以 CP=1 / headwise / all_gather 三条同镜像回归门槛把"依赖升级风险"与"算法切换风险"隔离。--gdn-cp-mode={auto,chunkwise,headwise,all_gather};auto在 v1 对现有合法配置保持原路由,非法 headwise 几何改为提前报错;chunkwise 仅显式启用;落地动态 CP group 解析和 micro-batch 级元数据复用;回退路径见第 7 节。本 RFC 重点覆盖:构造期能力声明与运行期路由分离;FLA 升级对现有 all_gather 路径本身数值的影响;权重同步/DCS 与 checkpoint 的 TP-only 分片不变式;以及"现有 8×H200 上真实 Qwen3.5-9B 的合法拓扑不会自然命中 fallback,需要使用 forced all-gather 受控对照"的实验设计(6.2)。
2. 上游 Chunkwise CP 能力与 Relax 现状
2.1 上游如何支持 Chunkwise CP
本 RFC 区分两个上游参考:
dev分支,对应 commit5139086e,提供 Chunkwise CP 的完整功能实现,是本任务的必选迁移基线;PackedSeqParams,解决5139086e在每层重复计算路线的问题。本 RFC 将其中的路线预构建最小子集作为可选性能改进,不把整个未合入 PR 当成稳定依赖。Chunkwise CP 的基础能力由 FLA 与 MCore 两层共同完成:
fla.ops.cp.build_cp_context,并让causal_conv1d、chunk_gated_delta_rule接受cp_context。卷积只交换每个连续分片边界的W-1个 token;GDN forward all-gather(h_ext, M)状态摘要并在本地折叠,backward 交换对应状态梯度。通信量由 head 几何决定,不再随完整序列激活线性增长。5139086e负责把该能力接入 MCore:新增context_parallel_layout.py完成 zig-zag ↔ contiguous 转换;在GatedDeltaNet.forward中解析 CP group、构造cp_context并传给 FLA kernel;在TransformerConfig中增加linear_cp_mode={headwise,chunkwise}及对应的 head 整除校验。上游执行链路为:
这条路径进入 GDN 前临时把 zig-zag 数据换成“每 rank 一段连续时间”,计算结束后再恢复 zig-zag,因此不会改变后续模型层的数据布局。上游同时支持 headwise 和 chunkwise:headwise 要求 heads 能被
TP×CP整除;chunkwise 只要求能被 TP 整除。5139086e有两个需要 Relax 额外处理的点:linear_cp_mode是模型构造期的静态配置,而 Relax 的 CP group 和 CP size 可以随 micro-batch 变化,不能直接用一个静态字段决定所有 forward。cu_seqlens计算相同的 token 搬运路线,并通过.item()等待 GPU 返回总 token 数;多个 GDN 层和 full recompute 会放大这部分开销。#5664 通过prebuild_thd_cp_partition_routes在数据准备阶段为每个 micro-batch 构建一次双向路线,后续层直接复用。考虑到本任务目标包含长上下文性能,RFC 默认建议同时移植 #5664 的路线预构建最小子集。但该优化不影响 Chunkwise 的数值语义:若维护者希望只依赖已合入代码,可以不纳入这部分,保留
5139086e的按需重建路线行为;其余 FLA/MCore 与 Relax 接入设计不变。2.2 Relax 当前实现与补丁情况
依赖与本地补丁
Relax 镜像固定 Megatron-Bridge
2faedbf6及其 MCore85bced0a,安装 FLA 0.4.1;构建时把 Bridge 与 MCore 源码合并到/root/Megatron-LM,再应用docker/patch/latest/megatron.patch。当前 patch 对 GDN 有两处必须保留的兼容修复:_prepare_qkv_for_gated_delta_rule会触发torch.compile/Dynamo 失败,因此仅对该 helper 禁用编译并回到 eager;get_parameter_local_cp使用param[tuple(slices)]表达多维基本切片,保证 headwise CP 的卷积参数、A_log、dt_bias沿正确维度切分。启动与模型构造
rollout_max_context_len / max_tokens_per_gpu推导本次任务可能使用的最大 CP,并向上取到 2 的幂;该值写入args.context_parallel_size,作为静态最大 CP。mpu.initialize_model_parallel(..., dynamic_context_parallel=True)在初始化阶段创建静态 CP 组及可复用的动态 CP 子组;forward 内不会临时创建 process group。linear_cp_mode,TransformerConfig.__post_init__固定按 headwise 要求 heads 被TP×静态CP整除。Relax 在模型构造前安装_relax_gdn_cp_config_assert:当 heads 只满足 TP 整除时,临时把 key/value head 数乘以 CP,使原生校验通过,随后恢复真实值。该操作只绕过构造检查。_patch_gdn_for_dynamic_cp替换GatedDeltaNet.forward,安装运行时路由 wrapper。每个 micro-batch 的执行流程
data.py根据该 micro-batch 的最长样本和每 GPU token 预算计算runtime_CP(1/2/4…),选择初始化阶段已创建的对应 CP 子组,并得到当前 rank 在组内的cp_rank。data.py先按cp_rank对每条样本做 zig-zag 切分,再拼成 rank-local THD tensor,构造batch["packed_seq_params"](data.py:409-442);input_ids和 attention mask,由 Bridge 在 embedding 后完成 CP/SP 切分,使用batch["vlm_packed_seq_params"](data.py:352-382、model.py:1039-1042)。cp_group、local_cp_size和全局边界写入最终PackedSeqParams;VLM 路径先把动态组和初始边界写入vlm_packed_seq_params,Bridge repack 后的最终对象和边界仍需在接入层重新确认。runtime_CP/group/rank:CP=1 或非 THD 走原生路径;THD CP>1 且 heads 被TP×runtime_CP整除时调用 MCore headwise;否则进入 Relax all-gather fallback。in_proj,再收集所有 CP rank 的投影并按 zig-zag 规则重组完整序列;每个 rank 重复执行完整卷积和 GDN scan,最后切回本 rank 的 zig-zag 输出并执行out_proj。该路径训练时要求 full recompute。fallback 首次使用 GPU
cu_seqlens时会转成 CPU list,并以_gdn_cu_seqlens_cpu缓存在当前PackedSeqParams(model.py:524-531);同一 micro-batch 的后续 GDN 层直接复用,避免每层重复.tolist()和 D2H。该缓存只服务旧 fallback 的序列重组/切片,不包含 Chunkwise 的布局 route 或cp_context。因此上游与 Relax 的核心异同是:
cp_contextresolve_cp_group优先读取PackedSeqParams.cp_groupdata.py每 micro-batch 选择 group/size,GDN wrapper 自行解析5139086e每次交换重建;#5664(Open)提出在数据准备阶段预建zigzag_to_contiguous_chunks约定输入为 zig-zag--allgather-cp已是连续切分--allgather-cp同开2.3 迁移结论
5139086e比 Relax 的 MCore 基线领先约 597 个提交,且 Relax 使用“Bridge + MCore 合并树 + 自有 patch”的装配方式,不能直接 cherry-pick。迁移应采用选择性 backport:build_cp_context以及支持cp_context的卷积/GDN kernel;context_parallel_layout、GDN chunkwise forward、linear_cp_mode,并补齐旧基线缺少的resolve_cp_group等前置能力;auto对现有合法配置保持原行为:CP=1/非 THD 走原生路径,THD CP>1 时可整除走 headwise、否则走 all_gather;非 THD 的非法 headwise 几何改为提前报错。chunkwise 仅显式启用,现有 all-gather fallback、TP-only 权重分片和 checkpoint 行为全部保留。2.4 备选方案与取舍
下一节据此给出 FLA 升级、MCore patch 生成和逐文件 backport 方案。
3. FLA/MCore 兼容方案
3.1 FLA 升级与验证
Relax 当前固定 FLA 0.4.1,但该版本没有 Chunkwise CP 所需的
fla.ops.cp,卷积和 GDN kernel 也不接受cp_context。因此docker/Dockerfile:41必须精确升级为flash-linear-attention==0.4.2;镜像构建后先做两类检查:
build_cp_context可以导入,并确认causal_conv1d、chunk_gated_delta_rule已支持cp_context参数;不满足则直接判定镜像构建失败。model.py:560-598)。因此候选镜像必须分别与旧镜像比较这三条路径的 forward 和 backward;任何一条数值回归,都不能把问题归因于 chunkwise 接入,而应先阻断 FLA/MCore 兼容层合入(具体门槛见 3.4)。报告记录最终安装的 FLA 版本、镜像 digest 和对应 tag commit
ca910f88,避免后续重建环境时出现版本漂移。3.2 Patch 方案
Relax 不直接在仓库中维护一份完整 MCore 源码。镜像构建时先组合固定版本的 Megatron-Bridge
2faedbf6与 MCore85bced0a,再把docker/patch/latest/megatron.patch应用到组合后的源码树(docker/Dockerfile:130-139)。因此 Chunkwise CP 的 MCore 改动也必须进入这份 patch。上游
5139086e比 Relax 的 MCore 基线新约 597 个提交,且gated_delta_net.py、transformer_config.py与 Relax 现有 patch 修改了相同区域,无法直接 cherry-pick。方案是在当前组合源码树上选择性移植第 3.3 节列出的能力,并重新生成一份包含“现有 Relax 修复 + Chunkwise CP backport”的完整 MCore patch。新的带日期 patch 保存到docker/patch/megatron/,docker/patch/latest/megatron.patch同步指向同一内容。镜像构建必须验证 patch 能干净应用、无冲突标记,并能导入新增的布局模块和 GDN 模块。提交说明同时提供文件/hunk 级清单:说明哪些上游改动被采用、哪些被排除,以及排除原因,方便维护者确认没有夹带无关的 MCore 升级。
3.3 逐文件 backport 清单
这是 MCore patch 的核心范围:
megatron/core/context_parallel_layout5139086e的 zig-zag ↔ contiguous 转换、THD rank 索引与 all-to-all 路由;可选但建议纳入:从 #5664 最小回移 route 的 build/get/prebuild/decode helper5139086e的按需构建megatron/core/ssm/gated_delta_net.pycp_context;把它传给卷积与 GDN kernelmegatron/core/transformer/transformer_config.pylinear_cp_mode字段,以及 headwise 按TP×CP、chunkwise 按 TP 校验 heads 的规则"chunkwise";由 Relax 在构造期声明可用能力,实际模式仍由运行时分发器决定megatron/core/packed_seq_params.pyresolve_cp_group;可选但建议纳入:#5664 的cp_partition_mode、zig-zag→contiguous / contiguous→zig-zag route tensor 字段local_cp_size/cp_group已存在于85bced0a,无需重复 backport;维护者不接受未合入优化时,只补统一 group 解析megatron/core/extensions/transformer_engine.pycu_seqlens分配同样长度的输出pad_between_seqs字段;只有本次 backport 同时采用该 padding 表示、且测试证明缺少此修复会导致输入/输出长度不一致时才纳入上游通过
pyproject.toml/uv.lock声明 FLA 依赖,但 Relax 的 MCore 是在 Docker 中组装而不是通过这些文件安装,因此同一依赖改动应落在docker/Dockerfile,不复制上游的包管理文件。上游布局与 GDN 测试会改编到tests/backends/megatron/;它们只验证被移植的基础能力,Relax 仍需第 5 节的自建测试覆盖动态 CP、VLM/unsplit 路径和现有 all-gather fallback。#5664 改动范围很大,本任务不整体迁移。默认只采用与 THD 路由复用直接相关的最小子集:
PackedSeqParams的布局/route 字段,route 的构建与读取 helper,以及布局转换消费预构建 route 的接口;不迁移其 GPT/Hybrid 全局布局规划、Attention/MTP/Qwen-VL 改造等其他内容。兼容层负责提供这套 MCore 接口,Relax 接入层只负责在data.py构造 micro-batch 时调用它。由于该 PR 尚未合入,实施时固定并记录实际采用的 head commit;若维护者不接受未合入代码,则删除这组可选 hunk 和data.py预构建调用,继续使用5139086e的按需构建逻辑。3.4 兼容层完成标准
本阶段只判断“FLA/MCore 能力是否正确迁移”,不验证 Relax 的动态 CP 路由和完整训练矩阵;后者统一放在第 5 节。完成标准为:
build_cp_context、布局转换模块和修改后的 GDN 可以正常导入;state_dict/sharded_state_dict参数键与分片维度保持不变,证明迁移没有改变 TP-only 参数和 checkpoint 语义。该阶段不修改默认 recipe,也不让
auto选择 chunkwise。4. Relax 接入方案
4.0 变更范围
relax/utils/arguments.py--gdn-cp-mode={auto,chunkwise,headwise,all_gather}及参数冲突校验,默认autorelax/backends/megatron/model_provider.pyprovider.finalize()前设置 MCore 的linear_cp_mode能力声明TransformerConfig用正确规则完成构造期 head 整除校验;普通 Attention 模型配置不变relax/backends/megatron/model.pyPackedSeqParams的 padded 边界并准备 routerelax/backends/megatron/data.pytests/backends/megatron/scripts/training/sft/run-qwen3.5-9B-math-dynamic-cp-8xgpu.shGDN_CP_MODE、SEED、RUN_ID,并让不同实验使用独立日志/checkpoint 路径scripts/benchmarks/4.1 模式与参数
上游只提供模型级的静态
linear_cp_mode,但 Relax 的实际 CP size/group 会随 micro-batch 变化,而且还需要保留自有 all-gather fallback。一个共享静态字段无法同时表达这些运行时选择;直接在 forward 中改写它又会让交错 micro-batch 或 recompute 读到错误状态。因此本方案把“模型构造期采用哪套校验规则”和“本次 forward 实际走哪条路径”分开:前者只作为能力声明,后者由 Relax dispatcher 通过 call-level override 决定。新增
--gdn-cp-mode {auto,chunkwise,headwise,all_gather},默认auto。四个模式的用途是:autoTP×runtime_CP整除时走 headwise,否则走 all-gather。不自动选择 chunkwise;非 THD 的非法 headwise 几何提前报错chunkwise--allgather-cp,非法组合直接报错headwiseall_gather构造期校验与运行时路由分离
MCore 回移
linear_cp_mode后,以该字段替代当前_relax_gdn_cp_config_assert的临时 head 数改写。构造期只需选择能正确放行当前模型的校验规则:显式headwise或静态几何可整除的auto声明为 headwise;其余模式声明为 chunkwise,采用 TP-only 校验。后者只用于让 all-gather/Chunkwise 可用的不可整除模型完成构造。实际执行路径仍由 dispatcher 按用户模式和每个 micro-batch 的
runtime_CP通过 call-level override 决定;auto继续保持“可整除走 headwise,否则走 all-gather”,不自动进入 Chunkwise。dispatcher 必须显式传入本次模式,不能依赖构造期默认值或在 forward 中改写共享 config;显式模式不做静默 fallback。4.2 动态 CP 接入流程
动态 CP 要保证同一个 micro-batch 的数据切分、布局路线和所有 collective 使用同一 CP size/group。接入流程如下:
context_parallel_size表示最大 CP。初始化阶段创建最大组及 size 1/2/4… 的动态子组;forward 不创建新 group。data.py根据最长样本和每 GPU token 预算选择子组,把cp_group和local_cp_size写入PackedSeqParams。二者必须同时存在且local_cp_size == cp_group.size()。padded_length % (2×runtime_CP) == 0。标准 THD 在data.py中按 runtime CP padding/slice;VLM/unsplit 必须等 Bridge 在 embedding 后完成最终 repack,再使用最终cu_seqlens_q_padded(没有时才用cu_seqlens_q)检查,不能复用 repack 前的临时边界。PackedSeqParams,供 FLAbuild_cp_context使用,避免每层 D2H。若采用 #5664,同时预构建双向 route;不采用时由5139086e在布局转换处按需构建。resolve_cp_group从该对象解析 group/size/rank;MCore 布局转换和 FLA kernel 均使用同一结果。PackedSeqParams随该 micro-batch 经过全部 GDN 层和 recompute,因此边界和可选 route 可复用。自定义数据路径未提供 CPU 边界时,首个 GDN 层允许做一次转换并打印 warning。最终长度不满足
2×runtime_CP时,在第一条布局 collective 前报错;动态 CP 正确性 gate 通过前不进行性能归因。4.3 dispatcher 重构与旧路径收敛
_patch_gdn_for_dynamic_cp重构为显式分发器:auto解析出 headwise 后也必须显式传入linear_cp_mode_override="headwise"。否则,当构造期为放宽校验而设置了linear_cp_mode=chunkwise、但某个较小runtime_CP又满足 headwise 整除时,直接调用原始 forward 会误走 Chunkwise。各分支的实际执行路径由第 5.2 节测试观测验证。现有代码按以下方式处理:
_AllGatherFullSequence负责带 autograd 的全序列收集,gdn_reassemble_full恢复顺序,gdn_cp_slice切回本 rank 输出;它继续作为显式all_gather模式和 Chunkwise 的 CP>1 正确性参照;_assert_gdn_full_recompute只在 all-gather 分支执行,因为只有该路径在每个 CP rank 保留完整序列激活;headwise/Chunkwise 不应继承这项限制;_relax_gdn_cp_config_assert的临时 head 数改写,由第 4.1 节的正式linear_cp_mode构造校验取代;_resolve_and_validate_cp_group取代当前_resolve_gdn_cp:内部调用 backport 的resolve_cp_group选择动态或静态组,再派生并校验 size/rank,避免 Relax 与 MCore 各自维护一套 group 解析逻辑。5. 正确性验证计划
参照基准为 CP=1 全序列 GDN;CP>1 另外使用现有 all-gather 路径交叉核对。数值门槛沿用对应上游测试:
< 2e-3(FLAtest_cp_gdn.py)< 1e-3(FLAtest_cp_conv.py)atol=2e-4, rtol=2e-3,cosine similarity ≥ 0.99999atol=2e-3, rtol=1e-2,cosine similarity ≥ 0.9999所有用例同时要求有效 token 数和 mask 精确相等,且输出、梯度无 NaN/Inf。
上述阈值以 FLA 0.4.2 和 Megatron-LM
5139086e的现有测试为起点;若目标 bf16 几何需要调整,必须在正式 before/after 运行前根据固定输入的误差分布一次性冻结并记录,不能在看到候选结果后放宽。5.1 布局与元数据单元测试
get_thd_context_parallel_rank_indices(..., "zigzag")与 Relaxslice_with_cp/gdn_reassemble_full的 token 所有权和顺序一致,避免两边对 zig-zag 定义不同;PackedSeqParams跨层/recompute 复用,换 micro-batch 或 CP 子组后使用新 route;PackedSeqParams均验证diff(route_cu) % (2×runtime_CP) == 0;测试包含 embedding/repack 前后边界变化,确保 route 绑定最终对象;TransformerConfig构造,确认第 4.1 节的构造校验能够替代_relax_gdn_cp_config_assert。5.2 真实 kernel 分布式测试(CUDA/NCCL)
真实 CUDA/NCCL 测试分两层:
cp_context的 FLAcausal_conv1d和chunk_gated_delta_rule,与无 CP kernel 比较输出和各输入梯度;序列长度覆盖 63/64/65 等 kernel chunk 边界,以及卷积宽度 W 附近的短分片。GatedDeltaNet,覆盖静态 SBHD(CP>1 时 batch=1)以及 packed THD 的单样本、多不等长样本、padding 尾;比较模块输入梯度和 conv/in_proj/out_proj/A_log/dt_bias参数梯度。几何覆盖 GQA(value heads 为 key heads 的整数倍)、key/value head dim 不同、TP/SP 开关和不可整除合成几何(如 4 key heads @ TP2/CP4)。四种用户模式必须通过 branch hook/counter 断言实际执行分支,不能只凭数值相近判断路由正确。另测试
chunkwise + --allgather-cp、deterministic、动态 SBHD、initial_state/output_final_state=True和非法 head 几何均按设计报错。5.3 并行 / 数据矩阵
动态 CP:静态 CP=4 组内 cp∈{1,2,4} 混合 micro-batch,断言逐 mb 使用正确子组、无跨子组 collective;四种模式在该矩阵下的路由分布可复现。
5.4 checkpoint 与 recompute
sharded_state_dict键集合和分片维度对旧镜像逐项相等,验证 GDN 参数仍为 TP-only;真实连续性使用同一 Qwen3.5-9B 配置做两条对照:A 连续训练 30 step;B 训练 20 step 后保存,在新进程恢复并继续 10 step。比较 step 21–30 的数据顺序、学习率、loss、grad norm 和最终参数;该用例通过后才满足摘要中的生产 gate。
5.5 端到端连续性
主 recipe 短训(≥30 step,CP=4):
auto与显式chunkwise的 loss 曲线、grad norm、有效 token 数三线对照;有效 token 数逐 step 精确相等。另选一个不含 GDN 的现有 CP recipe 做冒烟回归:默认
auto下不得进入任何 GDN mode/route 逻辑或预构建 THD route,确认普通 Attention CP 路径与当前版本一致。6. 性能与质量计划
6.1 冻结环境与运行纪律
nvidia-smi topo -m、CPU/RAM 和镜像信息;RUN_ID命名空间;测量期禁 resume(resume 测试独立目录、不计入性能);显存峰值统计 warmup 后 reset,启动期峰值单列;6.2 实验设计
E1(主实验:四方对照)。使用真实 Qwen3.5-9B、TP2/CP4/SP on 和同一份 SFT recipe:
headwise,隔离 FLA 升级和 MCore forward 重构的依赖影响;all_gather;chunkwise。比较 ④ vs ③ 衡量“用 Chunkwise 替换 fallback”的收益;④ vs ② 判断相对现有高效 headwise 是否非劣;② vs ① 单独量化依赖升级影响;④ vs ① 给出完整 before/after 结果。③ 与④首先使用相同 full-recompute 配置以保证公平,随后可增加④关闭 full recompute 的容量/性能实验。all-gather OOM 时作为容量结果报告,不虚构吞吐比。
Qwen3.5-9B 在现有 8×H200 资源的合法拓扑下会自然走 headwise,因此 forced all-gather 是受控算法对照,不代表默认线上路由。若后续能获得 32 卡,追加 TP2/CP16 的真实不可整除实验;否则用第 5 节不可整除合成几何完成正确性与 kernel 级性能验证,不把随机初始化模型作为 200-step 质量主实验。
E2(动态 CP)。先以真实模型连续 micro-batch 覆盖 cp∈{1,2,4},要求最终 padded 边界全部满足
2×runtime_CP整除且 forward/backward 通过;该正确性 gate 未通过时只报告 blocker,不进行动态 CP 性能归因。通过后再运行 dynamic-CP recipe,对比候选镜像auto与显式chunkwise,并报告逐 micro-batch CP 大小分布。E3(单层 shape crossover)。在同一 8×H200 TP2/CP4+SP 环境中,对单层 GDN 的 8K、16K、32K 序列分别比较 headwise 与 Chunkwise,使用独立进程和轮换顺序。该实验不替代完整模型结果,只用于识别布局转换与状态摘要通信随 shape 变化的性能拐点。
6.3 指标与统计口径
Σ(计量步有效非 padding token) / Σ(计量步各 rank 最大同步墙钟);nvidia-smi --query-gpu=timestamp,index,utilization.gpu,memory.used --format=csv,noheader,nounits --loop=11 Hz 逐卡原始样本留档;torch.cuda.max_memory_allocated / max_memory_reserved,8 张 H200 取最大;(h_ext, M)all-gather 用时;若采用路线预构建优化,同时确认 GDN 层内没有 route metadata 引发的重复 D2H/host wait;6.4 判定阈值(首个基线运行前冻结;开跑后如需变更则全配置重跑)
6.5 质量非回归
Σ label_nll / Σ valid_label_tokens);④相对①/②/③逐 seed 差值 ≤ 0.01 nats/token。6.6 报告与证据
最终报告包含:全部固定修订(Relax commit、镜像 digest、patch digest、FLA 版本、模型/数据修订)、完整命令与并行拓扑、第 5 节全部正确性结果与误差分布、6.2 全部运行(含失败/OOM)、逐对比值与对齐曲线、已知限制与第 7 节回退命令。
7. 回退路径
--gdn-cp-mode chunkwise,恢复默认auto。新版本的auto与当前 Relax 行为一致:可整除时走 headwise,否则走 all-gather。排查时也可显式指定headwise;不可整除几何则指定all_gather,并保留现有 full recompute 配置。正确性与性能报告记录新旧 Relax commit、镜像 digest、MCore patch digest、FLA 版本及对应 recipe,保证上述回退组合可以复现。
8. 风险与缓解
5139086e的按需构建linear_cp_mode与运行时实际模式不一致resolve_cp_group结果;group 只在初始化阶段创建;显式模式不静默回退2×runtime_CP,或 route 绑定了 VLM repack 前的旧边界PackedSeqParams上选择 padded 边界并做整除校验;真实 cp∈{1,2,4} gate 通过后才做动态性能实验auto现状并使用 headwise/all-gather 回退9. 请求维护者决策
5139086e为功能基线,选择性 backport 到 Relax 固定的 MCore85bced0a,而不整体升级 Megatron-Bridge/MCore?linear_cp_mode只用于模型构造,CP>1 的实际模式始终由 dispatcher 通过 call-level override 显式传入?--gdn-cp-mode={auto,chunkwise,headwise,all_gather}的命名和语义,其中auto保持当前 headwise/all-gather 路由,不自动启用 Chunkwise?auto模式下会选择 headwise,不会选择 all-gather。计划在同一配置下分别强制运行all_gather和chunkwise,用来比较 fallback 替换收益,同时保留 headwise 作为当前路径的对照。这个实验能否作为主要性能证据?如果要求使用auto模式本身就会选择 all-gather 的真实 Qwen3.5-9B 配置,则需要 32 卡 TP2/CP16;8 卡的不可整除合成配置只用于正确性和单层性能测试。data.py调用,保留5139086e的按需路线构建。