Skip to content

feat: precision verification framework + PP dump fix — diagnostic, comparison, and baseline tools for ON/OFF prefix-sharing - #40

Merged
Jackie2049 merged 62 commits into
mainfrom
parallel-precision-verify
Jul 16, 2026
Merged

Jackie2049 merged 62 commits into
mainfrom
parallel-precision-verify

Conversation

@boundless-future

@boundless-future boundless-future commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Overview

Build numerical precision verification infrastructure for prefix-sharing Phase 1, covering per-tensor to per-layer, single-card to TP/PP ON/OFF comparison. Fix PP dump silent loss in attention hooks.

Key Changes

  • diagnostic_dump{,_verl080}.py: dump 15+ intermediate tensors (hidden_states / QKV / RoPE / attention / logits / logprobs / entropy) via env-var-controlled hooks
  • cmp_diag_verl080.py: self-contained comparison engine — suffix-aligned packed compare, per-layer cosine, 2D element-wise, top-K error localization
  • assemble_dump.py: multi-rank (TP/PP) dump routing and offline assembly
  • cmp_baseline_{cross,within}_batch.py: GEMM precision baseline tools via multi-copy / cross-batch contrast
  • megatron_runtime.py / diagnostic_dump.py: PP stage-aware flush — each stage computes its own last-layer boundary instead of relying on global num_layers
  • flash_atten_npu_tnd: NPU flash-attention TND varlen + sparse_mode=3 backend
  • verl_mcore.py: nested_tensoras_nested_tensor for NPU autograd compat; add SKIP_RESTORE diagnostic probe

Test Results

  • CI lint + unit tests: ✅
  • NPU flash-attention 5-probe suite: ✅

Remaining Issues

  • PP dump assembly is offline (assemble_dump.py), not yet integrated with training pipeline
  • OFF-side invasive hooks (rope_freqs, pre-RoPE QKV) may need maintenance on mcore upgrades

boundless-future and others added 30 commits June 24, 2026 19:44
- 删除 test_logprob_extended.py:core.logprob 已删、函数无调用,测试成孤儿导致收集中断
- 移除 test_prefix_last_in_chain_reuse_resolves_to_ancestor_with_packed_slot 及其 helper:
  手工拼的 row2->row1(缺 slot 的 reuser) 链不可能由 trie 检测产生,且断言(解析到祖先)
  与 _resolve_provider_for_position 的 keep_start-1 扩展正确行为相反
- 新增 test_chain_reuse_resolves_position_to_provider_with_matching_label:
  用 [1,2,3,4,5]/[1,2,3,7,8]/[1,2,3,7,9] 自然链验证 seq2 位置2(token 3) 解析到
  直接 provider seq1(label 7) 而非 seq0(label 4),prefix-last 落在 seq1 真实 packed slot
…ec 数据结构

- verl_mcore/context/vocab_logprobs/observability: restore 由逐元素 2D 注入改为整段切片 + prefix-last 重算
- planner: 仅生成 prefix-last spec,删除 interior spec 与 is_shared_prefix_interior 标记
- 删除死字段 reuse_1d_pos、valid_indices 链、provider_predict_pos、reuse_first_suffix_label_pos
- 同步单测与 overview 文档;更新精度诊断工具
restore循环:i→reuser_idx、provider→provider_idx、idx→prefix_last_spec、prefix_last_by_row→prefix_last_spec_by_reuser、saved_key→saved_logits_key、provider_logits→saved_provider_logits;unfold/fold循环:row→seq_idx,lp_/ent_/logp_等隐晦缩写展开为log_probs_/entropy_;vocab_logprobs patch docstring收敛为prefix-last单语义。纯命名/注释调整,零行为变化。
last-attn/rope_freqs的ON dump失败提示从prefix_log.warning改为print,与同区块其它diag输出一致(直接走stdout)。
新增 flash_atten_npu_test.py,验证 sparse_mode=3 (rightDownCausal) 在 TND varlen 下对 reuser(Q<Skv) 的右对齐 causal。
- Probe A-E: provider-only mode2 sanity / 单reuser mode3(核心) / 全batch mode3单次调用 / 反向(128-tile) / seq>2048
- ground truth = TorchReferenceBackend;NPU-only,非NPU 干净 skip;__main__ 自带决策树
- 配套 plan: note 目录 npu-flash-attn-mode3-tnd-plan.md
新后端 flash_atten_npu_tnd (NpuFlashAttentionTndBackend):TND varlen + sparse_mode=3 (rightDownCausal),一次调用覆盖全 batch,reuser Q 仍 suffix-only。
- mode 3 原生支持 reuser(Q<Skv) 右对齐 causal(prefix 全见 + suffix causal),用现成压缩 [2048,2048] mask,不自建、不拆调用
- provider 段(Q==KV) 退化成标准 causal,与 baseline 一致
- 经 flash_atten_npu_test.py 5 probe 实测验证(A-E 全过,含反向 + seq>2048)
- 老 BSH 后端 flash_atten_npu 保留作回退;config 里 backend='flash_atten_npu_tnd' 切换
- 注册到 factory/config/__init__,加 test_factory_flash_atten_npu_tnd
attention() 里多写了一个 _,导致 ValueError: not enough values to unpack。
_prepare_flash_inputs 返回 8 元组 (q,k,v,cu_seqlens_q,cu_seqlens_kv,max_seqlen_q,max_seqlen_kv,pad_layout)。
实验文件走自己的 _varlen_tnd_call 没踩到,真实 backend 路径才暴露。
- 新增 --token POS(单个 int,默认 0),比较 packed[POS] 而非写死 packed[0]
- cmp_first_token → cmp_packed_token(dir_on, dir_off, pos, layer);_logits_first_token → _logits_at_pos;_first_token_metrics → _vec_metrics;_print_first_token → _print_packed_token
- 输出名/section 头不再有 first_token 字样:[packed_token] attn_L{layer}_pos{pos} / logits_pos{pos}
- --layer 同时控制 packed_token 的 attn 层(默认最后一层);logits 永远最后一层
- logits top-K 表按 val 排序、去掉 REL_ERR 列;attn 表保持原样
之前 packed[pos] 直接比 ON[pos] vs OFF[pos],但 ON 是裁剪后的 suffix-only packed、OFF 是完整 packed,两者 token 不对应——比较的是不同 token,结论无意义。

修复:新增 _aligned_vec_at_pos,先用 align_mask(OFF cu_seqlens + ON prefix_lens)把 OFF 的 suffix 段抽出来与 ON 对齐,再取 [pos]。pos 索引对齐后的 suffix-packed 空间(ON/OFF 一致,指向同一 token)。attn 和 logits 都走这条对齐路径,top-K 表同样先对齐。
abs 排序会把大负值 logit(softmax 后概率极低、不会被选中的 token)顶到表头,掩盖真正的高 logit 候选。改为 max(on, off) 带符号值排序,让候选 token 排前面。
仅改 1D _print_topk_vec 的 val 分支(logits/attn 表);2D logp/entropy 表的 val 仍用 abs(logp 本身为负,abs 有意义)。
新增环境变量 PREFIX_SHARING_FORCE_ZERO_PREFIX:
- prefix_detector.detect() 命中即早返回 0-prefix(所有 provider、无复用)
- verl_mcore build 旁路 has_sharing 早返回,让 ON pipeline 仍跑全路径:
  trim(0-prefix→不裁)/build_kv(全 provider→无注入)/attention(mode 3 全序列)/restore(no-op)
- 用途:跑 ON(0-prefix, mode 3, 全序列, 无裁剪/注入/位置补偿) vs OFF(mode 2, 全序列)
  ≈ → kernel mode 不是根因;偏差大 → kernel mode 是根因。
…nd-truth + skip-restore

S1.1: RoPE 外推自动诊断日志(step_min/max/mean/std + step_is_uniform)
  - 不需要 env var,每次外推触发时在 megatron_runtime.py 自动打印
  - step_is_uniform=False → 非线性 RoPE 调度,外推可能引入误差

S1.2: PREFIX_SHARING_DIAG_ROPE_GROUND_TRUTH=1
  - 从 inv_freq 直接计算完整频率表,跳过线性外推
  - 启用后 ON vs OFF 一致 → RoPE 外推是根因

S4.3: PREFIX_SHARING_DIAG_SKIP_RESTORE=1
  - 跳过 logprobs restore,直接返回 forward 原始输出
  - suffix 一致 → restore 是根因;不一致 → 偏差在 forward

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
新增 step01_vs_step12_maxdiff / is_linear 字段:
- 计算 pos_emb[0→1] 和 pos_emb[1→2] 的 step 差值
- maxdiff ≈ 0 → pos_emb[p] 对 p 严格线性 → 外推数学正确
- maxdiff > 0 → pos_emb[p] 对 p 非线性 → 外推引入误差

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ON path: dump_rope_emb_layer() called in _apply_positioned_rope after RoPE
OFF path: extract QKV, reconstruct standard 0..seg_len-1 position IDs,
         apply_rotary_pos_emb manually, dump via dump_rope_emb_layer()

Both paths write to rope_emb.pt for cmp_diag comparison.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…position-aligned comparison

1. rope_emb.pt 新增 positions 字段(per-token absolute position IDs)
2. cmp_rope_emb 重写:
   - first packed token Q/K 直接对比(row 0, pos 0,provider 必相等)
   - first row Q/K 按 position ID 对齐后逐位对比(0..L-1,两路径同)
   - reuser row 不直接对比(ON absolute pos vs OFF relative pos,设计差异)
3. ON 调用点传入 packed_position_ids
4. OFF 调用点传入重建的 segment-relative positions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dump: diagnostic_dump_verl080.py
  - dump_rope_emb_verl080(layer, query, key, num_layers)
  - 格式: rope_emb.pt = {layer: {"query": [T,H,D], "key": [T,H,D]}}
  - ON suffix-only / OFF 完整 packed,不需要 positions(suffix 对齐用已有元数据)

cmp: cmp_diag_verl080.py
  - cmp_rope_emb_layer(): per-layer Q/K cosine(suffix 对齐,同 cmp_attn_layer)
  - cmp_rope_emb_token(): 指定位置 Q/K 向量对比(同 cmp_packed_token)
  - _print_rope_emb_per_layer(): 每层 Q_cos + K_cos 报告

调用点:
  - ON: megatron_runtime.py → dump_rope_emb_verl080
  - OFF: attention.py → dump_rope_emb_verl080

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ge/cat)

OFF 路径 diag dump 用 torch.arange/torch.cat 构造每 segment 的 position IDs,
但 attention.py 顶部未导入 torch(与 forward_step.py:170 惯例一致),
导致 OFF 路径报 NameError: name 'torch' is not defined。
在 diag block 内局部 import torch,保持顶部零 torch 导入且非 diag 路径零开销。
OFF 路径 post-RoPE Q/K 之前用 get_query_key_value_tensors + 重建 positions +
index_select + 重算 apply_rotary_pos_emb,既脆又有 bug(_k_pos_emb or _q_pos_emb
对 tensor 求 bool 崩溃)。post-RoPE Q/K 是 original_forward 内部中间变量,唯一
拿到真实张量的办法是 hook。

- diagnostic_dump_verl080: 新增 capture_post_rope_qk() context manager,
  monkey-patch megatron.core.transformer.attention.apply_rotary_pos_emb,
  捕获 mcore THD prefill 每层 Q→K 两次调用的返回值,finally 还原。
  ON 不受影响(apply_rotary_pos_emb 在 megatron_runtime 自有命名空间,不走
  attention 模块全局)。
- attention.py OFF 分支:original_forward 外套 capture_post_rope_qk,
  直接取 captures[0]/[1] dump,删掉重算逻辑。
- 修潜伏 bug:dump_rope_emb_verl080 原手写 os.path.join 但未 import os,
  NameError 被 except 静默吞掉导致 rope_emb.pt 从未写盘;改用 _save_tensor。
  → ON/OFF 都需用本提交重跑一次。
- _print_shapes 加入 rope_emb.pt;_shape_of 支持嵌套 dict(每层 {query,key,
  positions}),显示 (dict,NL,Q(T,H,D)),一眼看出 ON(suffix-only)/OFF(full)
  token 数差异。
- cmp_rope_emb_token 的 pos 失败从笼统「无法对齐或越界」改为 _diag_rope_pos_fail
  三分:缺失 / 对齐失败(带 n_on,n_off,align_mask len,sum) / pos 越界(带对齐后
  token 数),定位 pos16 失败根因。
dump_rope_emb_verl080 的 flush 之前用 _save_tensor,但 _save_tensor 内部对入参
做 .detach().cpu().clone(),dict 没 .detach() → AttributeError 被其 except 静默吞掉,
rope_emb.pt 永不写盘(ON/OFF 都没写,cmp 显示 missing)。

da39661 原本想修 os 未导入的 bug,换成 _save_tensor 反而引入这个 dict 回归。
现仿 _flush_attn_buffer(写 attn_outputs.pt 的方式):rank0 + 直接 torch.save(dict)
+ 本地 import os,失败 print 不再静默。entries 插入时已 cpu clone,无需再搬。

注:用户目录里的 rope_emb_on.pt / rope_emb_off.pt 是旧版本残留,当前代码不产生
(grep 零命中),真正名字是 rope_emb.pt(dump 与 cmp 一致),需清掉残留重跑。
dump 侧(rope_preqk.pt,旋转前 Q/K):
- diagnostic_dump_verl080: 抽 _flush_dict_buffer 公用写盘 helper(避开 _save_tensor 对
  dict 失败的 bug);新增 dump_rope_preqk_verl080;capture_post_rope_qk → capture_rope_qk,
  每次 apply_rotary_pos_emb 同时存 {"pre":输入, "post":返回}。
- attention.py(OFF): hook 解包 pre/post,分别 dump rope_preqk.pt / rope_emb.pt。
- megatron_runtime.py(ON): _apply_positioned_rope 入口 dump 旋转前 Q/K。

cmp 侧(cmp_diag_verl080):
- _ROPE_STAGES=[pre,post];cmp_rope_emb_layer/token 改 stage 参数,支持单 stage 调用。
- rope_freqs 加 layer 过滤(cmp_rope_freqs(layer=...));新增 cmp_rope_freqs_token
  (指定 pos 的角度向量对比,max_abs 应为 0)。抽 _align_rope_freqs_layer helper。
- shapes 表加 rope_preqk.pt / rope_emb.pt;_shape_of 支持嵌套 dict。
- main 按 RoPE 计算顺序重排:per-layer 与 token 都是 pre Q/K → rope_freqs → post Q/K,
  定位分歧出现在哪一步(pre 偏=上游;freqs 偏=角度表;post 才偏=旋转应用)。
- pos 失败诊断细化(_diag_rope_pos_fail 三分:缺失/对齐失败/越界)。

注:layer 1-indexed(megatron self.layer_number 从 1 起),第一层用 --layer 1。
1) rope_emb → rope_postqk 改名(全仓 6 文件)
   rope_emb.pt 这名字误导(存的是旋转后 Q/K,非 embedding 表)。统一为 rope_postqk.pt,
   与 rope_preqk.pt(旋转前)配成一对。函数/变量/buffer/label 一并改:
   dump_rope_emb_verl080→dump_rope_postqk_verl080, cmp_rope_emb_*→cmp_rope_postqk_*,
   _ROPE_EMB_BUFFER→_ROPE_POSTQK_BUFFER, 结果名/section 头同步。
   判断 pre/post 用 "preqk" in name(postqk 不含 preqk,仍正确分流)。

2) rope_freqs: ON/OFF 统一 per-token,合并 dump 函数
   原 rope_freqs_on.pt(per-token) / rope_freqs_off.pt(raw 表) 内容不对称、文件名冗余
   (目录已区分 ON/OFF)。统一:两边都存 per-token 角度 rope_freqs.pt [T,1,1,D]。
   - dump_rope_freqs_on/off 合并为 dump_rope_freqs(ON/OFF 共用)。
   - OFF 调用方 index_select raw 表成 per-token 再 dump。
   - cmp 去掉「从 raw 表按 cu_seqlens 重建」逻辑,_align_rope_freqs_layer 简化为直接对齐。
   - shapes 表加 rope_freqs.pt;docstring 更新。

注:v070 cmp_diag.py 的 rope_freqs_on/off 引用保留(v070 dump 不写 rope_freqs,dead code)。
--topk 时 rope 三个 stage 的 dim 不再各自独立排序,而是以 rope_postqk 的 sort-err
top-K dim 为基准,rope_preqk 显示同样 dim,rope_freqs 显示 dim%D(角度按 head_dim
共享),便于逐 dim 追溯误差来源:
- preqk[dim] 已偏 → 上游(投影/hidden_states)
- freqs[dim%D] 偏 → 角度/位置 ID
- 两者都 ~0 但 postqk[dim] 偏 → 旋转应用本身

实现:
- _print_topk_vec 返回选中的 dim 列表。
- 新增 _print_vec_at_dims(按指定 dim 打印,不排序)。
- 新增 _load_rope_freqs_vec_at_pos(对齐后取 [pos] 角度向量 [D])。
- main rope topk 段:postqk Q/K sort-err top-K → preqk 同 dim、freqs dim%D。
- postqk 排序用 --sort-err(默认 abs),原先硬编码 val。
重构 OFF diag 块把 _off_positions 上提时漏抄了 device=(原 device=_off_q.device),
arange 默认建在 CPU,而 _q_pos_emb 在 GPU → _q_pos_emb.index_select(0, _off_positions)
抛 RuntimeError(device 不匹配)。该 diag 块无 try/except,异常直接崩 OFF forward,
导致 rope_freqs/rope_postqk/rope_preqk 三个文件都没写盘。

修:arange 显式 device=_cu.device(cu_seqlens 在 GPU),并用 int() 把 GPU 标量转
Python int 再给 arange。
定位「postqk 对但 attention_output 偏」的根因:dump attention 实际用的完整 KV,
逐元素比 ON(expanded) vs OFF(full)。

dump 侧:
- ON(megatron_runtime): build_kv 后 dump expanded_key/value → expanded_kv.pt
  (prefix 复用 + suffix,attention 实际用的完整 KV)。
- OFF(attention): dump 完整 K(hook 截的 post-RoPE K) + 完整 V(get_qkv,V 不旋转
  故忠实) → full_kv.pt。

cmp 侧(cmp_diag_verl080):
- cmp_attn_kv:ON expanded_kv.pt vs OFF full_kv.pt,逐层 K/V 分别 max_diff + cos,
  两者 reshape [T,-1],全量不需 align_mask。阈值极严(expanded 应精确==full)。
- _print_attn_kv 表插在 postqk 与 attn_output 之间。
- K/V 都 OK → attention 输入一致,偏差必来自 attention 计算/mask;
  K 或 V DIFF → bug 在 build_kv 的 prefix 复用。
- shapes 表加 expanded_kv.pt / full_kv.pt(per-side 文件,对侧显示 missing 正常)。
…call

之前 OFF 的 V 用 get_query_key_value_tensors 事后重算(re-call),而 K 走 apply_rotary
hook 是 in-context。数据显示 L1「K 精确(0)、V 偏 1.2e-4」的不对称——K 和 V 同出 get_qkv,
理应同对/同偏,怀疑是 re-call 时 hidden_states 已被 forward 的 in-place 操作改写导致 V 失真。

改:capture_rope_qk(attention_module) 额外 hook 实例方法 get_query_key_value_tensors,
把返回的 V(第 3 元素)in-context 截下。yield (qk_captures, v_captures)。
attention.py OFF 块传 self、解包、用 _v_caps[-1] 作为 full_kv 的 V(删掉 re-call)。

重跑后看 attn_kv 的 V_MAXDIFF:
- 若 V 也归零(和 K 一致)→ 之前是 dump artifact,expanded_kv 其实没问题;
- 若 V 仍偏 → ON 侧 V 真有问题,深挖 build_kv/store。
_orig_get_qkv 之前取成绑定方法(attention_module.get_query_key_value_tensors,self 已绑),
_capturing_get_qkv 又经 MethodType 绑定把 attention_module 作为 self_ 传入,原方法收到
两次 self,位置参数错位 → 'got multiple values for argument output_gate'。

修:_orig_get_qkv 改取未绑定的类方法函数 type(attention_module).get_query_key_value_tensors,
_capturing_get_qkv 显式接收 self_ 并传给未绑定原函数(调用方经 MethodType 自动注入 self_)。
postqk 对、build_kv 出来 V 偏,需区分 V 是 build_kv 之前就偏(get_qkv/hidden_states)
还是 build_kv 引入。在 megatron_runtime build_kv 调用前 dump 进去的 value(raw V,
get_qkv 出来、build_kv 之前),存 build_kv_input_v.pt。

cmp_build_kv_input_v:ON build_kv_input_v vs OFF full_kv V(suffix 对齐)。
输出 ON_T / OFF_T —— 若 ON_T < OFF_T 说明 ON 把 hidden_states 裁成 suffix-only。
判读:PASS(build_kv 前 V 一致)→ 偏由 build_kv 引入;FAIL(已偏)→ 根因在 get_qkv/hidden_states。
_rope_postqk_cos_for_layer 额外算 Q_max_diff / K_max_diff(对齐后整体 abs max)。
printer 表格改显 max_diff(替换 cos_min 列;cos_min 仍在 metrics 用于 WARN 判定)。

目的:验证 Q/K 的 max_diff 是否和 V 的 ~6e-5 一致。若一致 → Q/K/V 同源同偏(裁剪
matmul tiling);若 Q/K≈0 而 V~6e-5 → V 走了不同路径,需深查模型 attention 实现。
boundless-future and others added 12 commits June 30, 2026 10:58
… shape mismatch

- 2D: rel_max/rel_mean/abs_mean from actual per-pair diffs (not hardcoded)
- 2D: pearson from paired rows (fix shape mismatch 1024 vs 4096)
- 2D: _print_topk_2d for logp/entropy positions
- within_batch: same fixes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t entries

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cal wrappers

Added _print_plain_baseline and _print_rope_baseline wrappers
with SNG_T/STK_T or neutral column labels instead of ON_T/OFF_T.
within_batch uses within-batch pairwise headers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2D logp/entropy now compares row i vs row i+k*num_seq (same sequence
stack copies) instead of all C(N,2) pairs across different sequences.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…opy)

Was comparing row 0 vs row 1 (different sequences → huge false diffs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e quality)

- sd/md → single_data/multi_data
- cu_s/cu_m → cu_seqlens_single/cu_seqlens_multi
- st/mt → single_tensor/multi_tensor
- B → num_sequences, T_i → tokens_in_seq
- lyr → layer_index, n → stack_count
- i/k → seq_index/copy_index, j → multi_offset
- worst_md → worst_max_diff, layer_md → layer_max_diff
- on_f/off_f → single_flat/multi_flat
- q_all/k_all → first_cos_list/second_cos_list
- Consolidated comparison loops into DRY for-loops over file lists.
- Removed all single-letter variable names in logic functions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ag] ... #####

- attention.py (patch ON): added missing END marker
- megatron_runtime.py: unified ######### → ##### [PS-diag] format
- Megatron attention.py (invasive): added ##### wrappers to both blocks
- All 12 dump blocks now consistently wrapped for easy identification

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mble tool + variable readability refactor

## New features
- dump 侧 PP-aware routing: 替换 _rank0_only() 为 scope 模型 (global/tp_vocab/pp_last/pp_stage)
- 新增 _stage_last_layer() 解决 PP 下 buffer flush 永不触发的问题
- 新增 assemble_dump.py: 将多卡分散 dump 文件合并为单卡兼容格式
- 修复 forward_step.py PP 崩溃: restore_via_2d_unfold_verl080 加 isinstance guard

## Refactored
- 所有 dump 代码段变量名可读性优化 (_lp→log_probs_nested, _ol→original_lengths 等)
- cmp_diag.py / cmp_diag_verl080.py 变量名规范化 (lo/lf→logits_on/logits_off, lyr→layer_idx 等)
- exception 变量统一为 exc

## Files: 10 files (9 modified + 1 new)
…verl080.py

Key renames:
- fp→filepath, fa/fb→filepath_on/off, da/db→attn_dict_on/off
- ma/mb→meta_on/off, la/lb→layers_on/off
- d→attn_dict/rope_dict/kv_dict (context-specific)
- s→shard_tensor, t→tp_rank, pl_fp→prefix_lens_filepath
- _q→query_tensor, _name→result_name, _D→head_dim
- _sec→section_label, _stage→stage_label, _crop→crop_note
- _on_row1_end→on_row1_end, _cmp_len→compare_length, etc.
- Various underscore-prefixed locals→descriptive names
…assembly

Root cause: assemble_dump.py copied parallel_info.json verbatim from raw dump,
leaving tp_size/pp_size > 1 and scopes like 'tp_vocab'/'pp_stage'. cmp_diag then
tried to read logits_tp0.pt instead of logits.pt → showed (missing).

Fix: after merging all shards, update parallel_info.json to tp_size=1, pp_size=1,
all scopes='global', matching the single-card assembled directory.
…semble manifest rewrite

cmp_diag_verl080.py:
- _load_logits: remove TP vocab concat logic → just load logits.pt
- _logits_shape: deleted
- _load_manifest: deleted
- _print_topology: deleted
- _print_shapes: remove manifest params, treat logits like any other file
- main(): remove manifest loading and topology printing

Rationale: multi-rank assembly is done by assemble_dump.py before cmp runs;
cmp should work on flat single-card data only — no parallel awareness.

assemble_dump.py: revert the manifest rewrite (no longer needed since cmp
doesn't read manifest).
@boundless-future
boundless-future force-pushed the parallel-precision-verify branch 3 times, most recently from ce3b3c9 to 8b8a042 Compare July 1, 2026 13:30
@boundless-future
boundless-future force-pushed the parallel-precision-verify branch from 50310e4 to 5cab79d Compare July 14, 2026 08:22
…U autograd compat

On Ascend NPU, torch.nested.nested_tensor is intercepted by torch_npu and throws. verl/__init__.py unwraps it (via __wrapped__), but the unwrapped version does not support autograd backward on NPU tensors -- gradients are silently detached.

In PP training, this causes the last stage's loss backward to never reach the model, so no send_backward P2P is issued, and all prior PP ranks hang forever at recv_backward. TP doesn't hang because there is no P2P dependency, even though gradients are also broken.

Fix: replace all torch.nested.nested_tensor(rows, layout=torch.jagged) with torch.nested.as_nested_tensor(rows, layout=torch.jagged) in verl_mcore.py. The latter preserves the autograd graph correctly on NPU after unwrapping.

Affected functions:
  - _fold_2d_to_nested (root cause of PP hang)
  - _trim_nested_batch (forward batch trimming)
  - _trim_plain_batch_thd (forward batch trimming)

Co-Authored-By: Claude <noreply@anthropic.com>
@boundless-future
boundless-future force-pushed the parallel-precision-verify branch from b9904c0 to b7fe400 Compare July 14, 2026 12:33
…rify

# Conflicts:
#	prefix-sharing/prefix_sharing/integrations/megatron_runtime.py
#	prefix-sharing/prefix_sharing/integrations/verl_mcore.py
#	prefix-sharing/prefix_sharing/setup/patches/verl080_mcore0161_ms0160/vocab_logprobs.py
#	prefix-sharing/prefix_sharing/tools/cmp_diag_verl080.py
#	prefix-sharing/prefix_sharing/tools/diagnostic_dump.py
- Add missing _load_manifest function to cmp_diag_verl080.py (F821)
- Remove unused imports: Sequence, ensure_global_packed_token_lengths,
  inspect, torch, _cached_parallel_info, json, os (F401)
- Fix f-strings without placeholders to plain strings (F541)
- Remove unused variable device assignment (F841)

All flagged by ruff --select F,E9 (pyflakes + syntax errors).
@github-actions

Copy link
Copy Markdown

📊 Coverage report

prefix_sharing/setup/version_guard.py                  37      9    76%   41, 44, 52, 55, 62-66
prefix_sharing/utils.py                                 7      0   100%
---------------------------------------------------------------------------------
TOTAL                                                2509    868    65%

Changed files in this PR

Name                                              Stmts   Miss  Cover   Missing
-------------------------------------------------------------------------------
prefix_sharing/backends/__init__.py                   9      0   100%
prefix_sharing/backends/factory.py                   20      0   100%
prefix_sharing/backends/flash_atten_npu_test.py     223    223     0%   28-464
prefix_sharing/backends/flash_atten_npu_tnd.py       67     43    36%   66-73, 80-84, 94-101, 132-133, 142, 157, 191-257
prefix_sharing/core/config.py                       116     10    91%   40-41, 46-52, 82, 97, 99, 130, 132
prefix_sharing/core/prefix_detector.py              112      4    96%   82, 166, 168, 187
prefix_sharing/integrations/megatron_runtime.py     158     75    53%   34-35, 37, 39, 41, 114-115, 137-138, 183-217, 219-249, 257-290, 313, 323-324, 348-349, 368, 391, 404
prefix_sharing/integrations/verl_mcore.py           386    170    56%   55-56, 89-92, 119-120, 130-131, 138-150, 160-161, 178-179, 282, 288, 294, 313, 333, 398-399, 418, 534, 537, 549, 553-557, 600-702, 716-762, 778-828, 837-846, 860-866, 883-902, 921-923
-------------------------------------------------------------------------------
TOTAL                                              1091    525    52%

@boundless-future boundless-future changed the title Parallel precision verify [feat] precision verification framework + PP dump fix — diagnostic, comparison, and baseline tools for ON/OFF prefix-sharing Jul 15, 2026
@boundless-future boundless-future changed the title [feat] precision verification framework + PP dump fix — diagnostic, comparison, and baseline tools for ON/OFF prefix-sharing feat: precision verification framework + PP dump fix — diagnostic, comparison, and baseline tools for ON/OFF prefix-sharing Jul 15, 2026
@Jackie2049
Jackie2049 self-requested a review July 16, 2026 07:28

@Jackie2049 Jackie2049 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File-level review comments (1/2)

每条 comment 罗列该文件的开发目标及对应修改。

@@ -1069,6 +1069,23 @@ def forward(
value = value.squeeze(1)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标 A:OFF 路径插桩 pre-RoPE Q/K/V + hidden_states dump(post-squeeze、pre-rotary)

  • dump_rope_preqk_verl080(layer_number, query, key, num_layers)
  • dump_build_kv_input_v_on(layer_number, value, num_layers)
  • dump_hidden_states_on(layer_number, hidden_states, num_layers)

开发目标 B:OFF 路径插桩 post-RoPE Q/K + full KV dump(rotary 后、core attention 前)

  • dump_rope_postqk_verl080(layer_number, query, key, num_layers)
  • dump_full_kv_off(layer_number, key, value, num_layers)

两者由 PREFIX_SHARING_DIAG_DUMP env var 控制,未设时仅一次 os.environ.get 检查。try/except 保护 dump 失败不崩训练。

@@ -1354,8 +1354,26 @@ def fit(self):
max_prompt_length=self.config.data.max_prompt_length,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:在 fit() 训练循环前注入 synthetic baseline 数据

  • 第 1354-1374 行:读 PREFIX_SHARING_BASELINE_SYNTHETIC / NUM_SEQ / STACK / SEED env var
  • 调用 inject_baseline_synthetic.patch_baseline_synthetic() 替换 dataloader
  • 用于无真实数据加载器的 CI 环境

from prefix_sharing.backends.base import BackendCapabilities, PrefixAttentionBackend, PrefixDeltanetBackend
from prefix_sharing.backends.block_causal_mask import build_block_causal_mask
from prefix_sharing.backends.factory import get_backend_instance
from prefix_sharing.backends.flash_atten_base import FlashAttentionMixin, FlashBackendValidationError

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:导出新的 NPU TND varlen + sparse_mode=3 backend

  • 导入 NpuFlashAttentionTndBackend(第 89 行)
  • 加入 __all__(第 97 行)

@@ -0,0 +1,257 @@
"""Ascend NPU Flash Attention backend — TND varlen + sparse_mode=3 (rightDownCausal).

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:实现 NPU TND varlen + sparse_mode=3 attention 后端

替代旧 BSH + per-sample B1SS mask + sparse_mode=1 方案(reuser 上结果错误)。利用 TND varlen + sparse_mode=3 rightDownCausal 的"Q<KV 右对齐"语义,一次调用覆盖 provider(标准 causal)和 reuser(prefix 全可见 + suffix causal)。

  • _compressed_causal_mask():全局缓存的 [2048,2048] 下三角 mask
  • apply_rope(query, key, plan, ...):按 position_offsets 逐 token 应用 RoPE
  • build_kv(key, value, store, plan, ...):与 TorchRef 相同的 expanded KV 语义
  • attention(query, ek, ev, plan, ...)input_layout="TND"sparse_mode=3actual_seq_qlen/kvlen=cu_seqlens

@@ -0,0 +1,468 @@
"""TND (varlen) + sparse_mode=3 (rightDownCausal) 可行性实验(NPU only)。

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:5-probe 验证矩阵系统性验证 TND varlen + sparse_mode=3 的可行性

  • Probe A (provider-only mode 2):baseline sanity——接线正确验证
  • Probe B (single reuser mode 3):核心验证——mode 3 rightDownCausal 在 varlen 下对 Q<KV 真的右对齐
  • Probe C (full batch mode 3):单次 TND 调用同时处理 provider+reuser(vs 退路:拆两次调用)
  • Probe D (full batch backward):128-tile 约束下 gradient 对齐
  • Probe E (seq>2048):边界验证——压缩 mask [2048,2048] 下超长序列兼容性

各 probe 输出 {name, pass, detail, out_diff, grad_diff} + 决策树建议。

"""

if not self.enable_prefix_sharing:
return

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:将 flash_atten_npu_tnd 加入支持的 backends 列表

  • _SUPPORTED_BACKENDS 新增 "flash_atten_npu_tnd"(与 factory 注册同步)

@@ -106,6 +106,15 @@ def prefix_attention(
f"built expanded kv: expanded_key_shape={tuple(expanded_key.shape)}, expanded_value_shape={tuple(expanded_value.shape)}"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标 A:修复 PP dump 中 attention output 永不 flush 的 bug(核心 bug 修复)

旧逻辑用 layer_number == num_layers 判断最后层——PP stage 最大 layer_number < num_layers,永远不 flush。

  • 新增 _last_layer_for_stage(layer_number, num_layers, pp_size):按 PP partition 计算当前 stage 的最大层号
  • flush 条件改为 layer_number == _last_layer_for_stage(…)
  • pp_size=1 时退化为旧逻辑,完全兼容

开发目标 B:新增 SKIP_RESTORE env var 诊断 probe

  • restore 前检查 PREFIX_SHARING_SKIP_RESTORE:设值时跳过 restore,用于隔离"分歧在 restore 前/时"
  • 添加 [PS-diag] 打印警告

@@ -35,7 +35,6 @@
from prefix_sharing.integrations.parallel_info import get_megatron_parallel_info

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标 A:迁移 NestedTensor API 实现 NPU autograd 兼容

  • nested_tensor(seqs, layout=torch.jagged)as_nested_tensor(rows, layout=torch.jagged)(~6 处)
  • NPU ascend 不支持 nested_tensor 的 autograd

开发目标 B:新增 SKIP_RESTORE 诊断 probe

  • restore 路径前加 env var 检测分支

@Jackie2049 Jackie2049 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File-level review comments (2/2)

每条 comment 罗列该文件的开发目标及对应修改。

@@ -36,7 +36,11 @@ def patched_forward(
ctx = current_prefix_sharing_context()

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标 A:解耦 per-layer diagnostic dump 检测,支持 PP 感知的 flush

  • 新增 _compute_stage_last_layer 辅助函数
  • 提取 _dump_attn_output 薄委托层从 _FSDP_ATTN_BUFFER 分离
  • 新增 expanded_kv / full_kv / attn_inputs 的 per-layer dump 委托点

开发目标 B:添加 layer_index/config 诊断日志

  • patch 启动时打印 [PS-diag] num_hidden_layers={n}, layer_idx={idx}

@@ -122,57 +122,58 @@ def patched_forward_step(
# ON: prefix_lens / original_lengths 取自 plan;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:在 forward_step ON/OFF 路径衔接处新增 per-layer diagnostic dump 接线

  • dump_meta_verl080() / dump_attention_mask_verl080() / dump_label_mask_verl080() 于 build_micro_batch 前后
  • dump_fsdp_model_output_2d_verl080() 于 restore 前后
  • dump_logprobs_2d_verl080() / dump_entropy_2d_verl080() 于 loss 前后
  • import 路径同步更新

@@ -26,9 +26,9 @@ def patched_fn(logits, labels):
# ##### [PS-diag] dump logits(ON/OFF 都 dump,必须在 original_fn 之前) #####

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:同步诊断 dump 的 logits 处理路径

  • logits 前后嵌入 dump_logits_verl080(logits) / dump_logprobs_verl080(logits, labels)
  • 对称包装 ON/OFF 两端 logits dump

@@ -0,0 +1,196 @@
"""Multi-rank diagnostic dump assembler — merges PP/TP-sharded files into a

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:多卡(TP/PP)诊断 dump → 单卡格式离线拼装

  • TP 拼装:按 _tp{r}.pt 后缀识别 shard,vocab 维拼接 logits
  • PP 拼装:按 _pp{p}.pt 后缀合并层 dict
  • 单卡 fast path:TP/PP=1 直接 copy

⚠️ 此版本不含 DP 合并(与 open-source-minireport 分支版本相比不够完整)

@@ -0,0 +1,679 @@
"""GEMM Precision Baseline — Cross-Batch-Size Comparison.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:"跨 batch"精度基线——测量同一配置下两次独立运行之间的自然噪声底

GPU GEMM 非确定性导致相同输入多次 forward 有微小差异。此差值就是 ON/OFF 对比时应排除的噪声底。

  • _compare_attn_cross_batch():per-layer Q/K cosine + max_diff
  • _compare_logits_cross_batch():packed logits 按 cu_seqlens 切片对比
  • _compare_2d_cross_batch():2D tensor 逐元素对比 + pearson
  • CLI:--dir-single --dir-stacked

@@ -7,7 +7,7 @@

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:v080 精度对比引擎全面增强——3 大维度

RoPE 对比体系:cmp_rope_postqk_layer/token()(pre/post 两阶段)、cmp_rope_freqs()、_rope_postqk_vec_at_pos()、_diag_rope_pos_fail()

KV 对比体系:cmp_attn_kv()(expanded vs full)、cmp_build_kv_input_v()、_print_attn_kv()

Suffix 对齐统一:删除 2D attention_mask 对齐(_build_alignment_mask_from_2d、_load_attention_mask_2d),全改为 prefix_lens + cu_seqlens

代码清理:_first_token_metrics→_vec_metrics 重命名、变量名规范化、删除旧 logging

@@ -10,7 +10,7 @@

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:新增 OFF 侧诊断 dump 函数 + 统一 attention output buffer 管理

  • 新增 dump_fsdp_attn_output(output, module):_FSDP_ATTN_BUFFER buffer(layer1 清空 / last layer flush)
  • 新增 dump_rope_pre_post_qk(rope_dict_on, rope_dict_off, ...)
  • 新增 _rank0_only() 门控:多 rank 下仅 rank 0 存盘
  • 新增 _get_dump_dir():从 env var 统一获取 dump 目录

@@ -22,6 +22,7 @@

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:新增 6 个 OFF 侧侵入式 dump 函数

  • dump_rope_preqk_verl080(layer_number, query, key, num_layers) → rope_preqk.pt
  • dump_rope_postqk_verl080(layer_number, query, key, num_layers) → rope_postqk.pt
  • dump_full_kv_off(layer_number, key, value, num_layers) → full_kv.pt
  • dump_build_kv_input_v_on(layer_number, value, num_layers) → build_kv_input_v.pt
  • dump_hidden_states_on(layer_number, hidden_states, num_layers) → hidden_states.pt

每函数带 try/except 保护、_rank0_only() + _FSDP_ATTN_BUFFER 协议

@@ -0,0 +1,115 @@
"""Inject synthetic data for GEMM precision baseline — reuse nested-prefix

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:生成合成 baseline 数据,供 CI 环境验证精度比较工具链

  • patch_baseline_synthetic(trainer, json_path, max_prompt_length, max_response_length, num_seq, stack, seed)
    • 构造填充后 input_ids / attention_mask / sequences 等 tensor
    • stack 参数控制数据规模(相同数据重复 K 次)
    • 挂载到 trainer.train_dataloader 替换真实加载器


import pytest

from prefix_sharing.backends.factory import get_backend_instance

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发目标:为 flash_atten_npu_tnd 后端添加 factory 单元测试

  • 新增 test_factory_flash_atten_npu_tnd():验证后端创建及其 build_kv/attention 签名
  • skip 门控:仅在 flash_atten_npu_tnd importable 时运行

…est backends

Delete flash_atten_npu_tnd.py and flash_atten_npu_test.py, and clean up all references in __init__.py, factory.py, config.py, and tests.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

📊 Coverage report

prefix_sharing/setup/version_guard.py                  37      9    76%   41, 44, 52, 55, 62-66
prefix_sharing/utils.py                                 7      0   100%
---------------------------------------------------------------------------------
TOTAL                                                2215    602    73%

Changed files in this PR

Name                                              Stmts   Miss  Cover   Missing
-------------------------------------------------------------------------------
prefix_sharing/backends/factory.py                   17      0   100%
prefix_sharing/core/prefix_detector.py              112      4    96%   82, 166, 168, 187
prefix_sharing/integrations/megatron_runtime.py     158     75    53%   34-35, 37, 39, 41, 114-115, 137-138, 183-217, 219-249, 257-290, 313, 323-324, 348-349, 368, 391, 404
prefix_sharing/integrations/verl_mcore.py           386    170    56%   55-56, 89-92, 119-120, 130-131, 138-150, 160-161, 178-179, 282, 288, 294, 313, 333, 398-399, 418, 534, 537, 549, 553-557, 600-702, 716-762, 778-828, 837-846, 860-866, 883-902, 921-923
-------------------------------------------------------------------------------
TOTAL                                               673    249    63%

@Jackie2049
Jackie2049 merged commit 35ede9e into main Jul 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants