Skip to content

feat: plan collector ops once for every backend - #676

Draft
tianhaox wants to merge 47 commits into
ai-dynamo:mainfrom
tianhaox:feat/collector-v4-gemm-plan
Draft

tianhaox wants to merge 47 commits into
ai-dynamo:mainfrom
tianhaox:feat/collector-v4-gemm-plan

Conversation

@tianhaox

@tianhaox tianhaox commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

#649 has merged. The branch now sits linearly on main (e8cbe2d) and holds only this PR's own commits, each signed off for DCO. The rebuild kept every commit's diff: commits 15–44 have the same trees as before, commits 1–14 the same patches on the new base, and commits 46–47 the same patches after the new commit 45. Evidence below names the commits the runs used; the list under the table maps them to the current IDs.

How to review

One commit per step; each builds on the previous one. Against main the forty-seven commits change 273 files, +10,392 / −16,712 lines.

# Commit Size Read first
1 4ffd4ee replace the collector evidence mechanism with collector notes 28 files, +710 / −1,493 .claude/rules/collector/overview.md, collector/notes/*.yaml, R8 in tools/perf_database/check_collector_data.py
2 5b38572 plan GEMM cases once for every backend 23 files, +680 / −261 the three collect_gemm.py getters, capabilities.yaml backend_gaps
3 b3fd46e plan GEMM through a generic op plan 17 files, +484 / −350 collector/op_plans.py, tests/unit/collector/test_op_plans.py
4 bf5c03e plan MoE cases once for every backend 45 files, +916 / −1,657 the MoE section of op_plans.py, cases/base_ops/moe.yaml, the new backend_gaps and guard notes
5 a51f72a plan compute_scale and encoder attention once for every backend 15 files, +247 / −223 the two sections of op_plans.py; the getters are one line
6 c37b32d declare the kernel dispatch in op plans 6 files, +158 / −29 OpPlan.dispatch, the new checks in test_op_plans.py
7 8cf76d9 plan the linear-attention ops once for every backend 10 files, +197 / −264 the GDN, KDA and Mamba2 sections of op_plans.py
8 9602c46 plan the MLA generation BMMs once for every backend 8 files, +83 / −66 the MLA BMM section, the vLLM fp8 backend gap
9 3b8e90d plan MLA attention once for every backend 9 files, +225 / −296 the MLA section: rows built on demand
10 4563ab7 plan the mHC module once for every backend 13 files, +245 / −146 the mHC section, _run_case_id in collect.py, the architecture backend gaps
11 add3332 leave out the TRT-LLM MoE regions no implementation serves 3 files, +45 / −9 the TRT-LLM backend_gaps and their two notes
12 a7cb766 describe dict rows, rows built on demand and the mHC region field 2 files, +8 / −3 case_authoring.md, "Op plans"
13 1e09e38 record the SM120 run behind the TRT-LLM MoE gaps 1 file, +20 / −11 the two TRT-LLM MoE notes
14 fceee6b collect the Llama 3.1 405B attention shape 2 files, +17 / −2 cases/models/LlamaForCausalLM_cases.yaml
15 7cf4076 keep the cases a collection skips in one commented list 48 files, +827 / −1,255 collector/skips.py, plan() in op_plans.py, test_skips.py
16 789fb23 stop a measurement child whose worker died before PDEATHSIG was armed 2 files, +49 / −2 _die_with_worker_preexec in collector/helper.py
17 901e532 count MiMo-V2-Flash and Llama 4 attention layers as frameworks serve them 4 files, +104 / −37 sdk/utils.py, the MiMo record in goldens/engine_step.json
18 0d3ee48 plan attention once for every backend 25 files, +1,194 / −2,317 the attention section of op_plans.py, the attention entries in skips.py, cases/base_ops/attention_*.yaml, test_attention_plan.py
19 c62774f describe skip entries that name a dispatch kernel 2 files, +2 / −1 layer_permissions.md, case_authoring.md
20 575c608 measure several module points per task on TRT-LLM 7 files, +835 / −163 collector/trtllm/module_series.py, test_module_series.py
21 3688cc8 label TRT-LLM MLA context rows with an FP8 KV cache as FP8 compute 31 files, +70 / −60 collect_mla_module.py, op_backend_facts.yaml; 26 relabelled tables
22 6bf4fb9 collect DeepSeek-V4 attention with the FP8 KV pool TRT-LLM serves on Blackwell 3 files, +18 / −20 collect_dsv4_attn.py
23 9a58971 resolve bfloat16 MLA FMHA on an FP8 KV cache to FP8 on TRT-LLM 7 files, +174 / −8 resolve_trtllm_mla_compute in sdk/models/helpers.py, test_context_fmha_compat.py
24 2ec7dfa give Step-3.7-Flash its sliding-attention heads and context length 4 files, +76 / −2 the two stepfun-ai--Step-3.7-Flash*_config.json, THIRD_PARTY_NOTICES.md
25 796cf34 let one op-plan task time several cases with batch rows 2 files, +68 / −12 batch_rows in op_plans.py, test_op_plans.py
26 3318b5c plan the MLA context and generation modules on op plans 11 files, +329 / −23 the MLA module section of op_plans.py, vllm/collect_mla_module.py, cases/base_ops/mla_module.yaml
27 1572e34 plan the DSA context and generation modules on op plans 19 files, +1,066 / −1,739 the DSA section of op_plans.py, the DSA entries in skips.py, the three DSA collectors
28 b97458e plan SGLang's DSA skip-indexer modules on op plans 3 files, +27 / −20 the skip-indexer plans
29 8ebd8d2 plan the MSA context and generation modules on op plans 15 files, +712 / −1,380 the MSA section of op_plans.py, the MSA entries in skips.py, the three MSA collectors
30 43d7505 keep the kernel-limit class of points SGLang module subprocesses refuse 4 files, +24 / −10 the traceback lookup in sglang/collect_mla_module.py and sglang/collect_msa_module.py
31 b8d3a27 plan the DeepSeek-V4 attention modules on op plans 17 files, +927 / −1,396 the DeepSeek-V4 section of op_plans.py, sglang/runtime_limits.py, the three collect_dsv4_attn.py
32 fd6c281 plan the DeepSeek-V4 sparse kernels on op plans 10 files, +250 / −474 the paged MQA logits and top-k calibration plans, sglang/deepseekv4_sparse_modules.py
33 d95bbcd plan the wide-EP moe_ep and DeepEP ops on op plans 12 files, +407 / −479 the wide-EP section of op_plans.py, its skips.py entries
34 513d775 keep one moe_ep case per table key 5 files, +50 / −38 _expand_moe_ep
35 c3811fa skip vLLM FP8-compute MLA context cases outside the SM100 family 1 file, +4 / −4 the MLA module entry in skips.py
36 cc8e2d4 hash the collector source once instead of per-module closures 37 files, +154 / −2,510 collector/provenance.py, collector-check.yml
37 7827e08 keep deviation notes as comments at the deviating code 21 files, +107 / −523 the six comments listed in section 37, check_collector_data.py
38 b71c16d plan the GLM-5 sparse kernels on op plans 12 files, +158 / −453 the GLM-5 sparse plans, sglang/deepseekv4_sparse_modules.py worker points
39 7eac32c plan the DeepSeek-V4.1 native module on an op plan 4 files, +60 / −34 the dsv41 plan
40 d66ee7d keep the DeepSeek-V4 top-k calibration on Flash through skips.py 5 files, +23 / −70 the calibration entry in skips.py
41 ba9820d refuse XPU attention KV pools the device cannot hold at run time 8 files, +65 / −49 run_attention_torch in vllm/collect_attn_xpu.py, layer_permissions.md
42 fc923fc move the static kernel-limit guards of planned ops into skips.py 10 files, +86 / −199 the new skips.py entries, MlaCase.local_heads
43 8189854 drop the CI contract checks for the removed collector comparison base 1 file, +2 / −11 tests/test_ci_workflow_contracts.py
44 651832b skip TRT-LLM sparse MLA modules on SM120, which need the fp8_ds_mla pool 5 files, +21 / −32 the SM120 entry in skips.py
45 58f67d6 calibrate the DeepSeek-V4-Pro top-k by default 4 files, +7 / −15 the calibration entry gone from skips.py, test_dsv4_common_test_cases.py
46 c410856 rewrite the collector rules for op plans and one skip list 10 files, +404 / −490 .claude/rules/collector/overview.md, then layer_permissions.md
47 4d8ecf2 drop version narration from collector comments and docstrings 116 files, +666 / −685 comments only
Commit IDs before the DCO rebuild
# Now Before
1 4ffd4ee 175a982
2 5b38572 b4787fc
3 b3fd46e 82e99c3
4 bf5c03e b287241
5 a51f72a 2e771dd
6 c37b32d b012f30
7 8cf76d9 9f1132f
8 9602c46 84ba2f3
9 3b8e90d 41777a4
10 4563ab7 b848721
11 add3332 0578e35
12 a7cb766 97ecd0e
13 1e09e38 57a3da8
14 fceee6b 88c0f92
15 7cf4076 014947d
16 789fb23 d1d24b9
17 901e532 5aeba48
18 0d3ee48 4622201
19 c62774f df93541
20 575c608 1bf5a89
21 3688cc8 7d925f6
22 6bf4fb9 d6a95a4
23 9a58971 af48fda
24 2ec7dfa 1a2ef69
25 796cf34 338b9a6
26 3318b5c d47193d
27 1572e34 bbcb8bd
28 b97458e 8de87d6
29 8ebd8d2 a8eaf74
30 43d7505 93d9976
31 b8d3a27 bd95af8
32 fd6c281 b9686f6
33 d95bbcd 8dc88fc
34 513d775 49351ef
35 c3811fa fbb4756
36 cc8e2d4 d88e9e2
37 7827e08 dbd831a
38 b71c16d 3144d80
39 7eac32c cc4c942
40 d66ee7d 3e76f72
41 ba9820d 0e5c8ef
42 fc923fc 10842d5
43 8189854 4c26239
44 651832b b5d9582
45 58f67d6 new
46 c410856 cdd6f84
47 4d8ecf2 f3a9a27

Commit 3 replaces commit 2's GEMM-specific plan code with the generic one. Commits 4, 5 and 7–10 add ops; most of commit 4's line count is MoE YAML policy and tests. Two commits touch the mechanism: commit 6 declares the kernel a backend measures instead of picking it inside rows, and commit 9 builds rows on demand so that rows may hold framework objects while planning stays framework-free. Commit 10 lets --plan-only hash {id, params} rows by the ID a run records. Commit 11 turns three MoE regions the GPU validation found unservable on TRT-LLM into backend gaps; commit 13 adds the SM120 run to their notes. Commit 14 adds one attention shape through a model profile; attention itself stays on its getters. Commit 15 replaces the capability floors, backend gaps, guard notes and the empty denylist with one commented list, collector/skips.py, and adds four TRT-LLM model entries. Commit 16 is a follow-up to a #649 review comment. Commit 17 is the only SDK change: two layer-type fixes found while checking which models read the attention tables. Commit 18 moves attention onto the op plans. Commits 19–22 prepare the module ops: the TRT-LLM series runner, and two collector-side mismatches with what the SDK looks up (TRT-LLM MLA labels, DeepSeek-V4 KV on Blackwell). Commits 23–24 are SDK-side: TRT-LLM MLA FMHA resolution and the Step-3.7-Flash configuration. Commits 25–26 add batch rows and move the MLA attention modules onto op plans; commits 27–28 move the DSA modules and commit 29 the MSA modules. Commit 30 fixes how SGLang module refusals are classified. Commits 31–32 move DeepSeek-V4, and commits 33–34 the wide-EP ops. Commits 35–37 simplify: one SM120 skip, one collector hash, and the deviation notes as code comments. Commits 38–39 move the last SGLang-only ops. Commit 40 moves the DeepSeek-V4 calibration's model choice into skips.py, commit 41 retires the last plan-time memory filter, and commit 42 stops planning cases that fail on every run. Commit 43 updates three CI contract tests for the comparison base that commit 36 removed. Commit 44 settles TRT-LLM DSA on SM120 with a GPU run, and commit 45 calibrates DeepSeek-V4-Pro's top-k by default. Commit 46 rewrites the collector rules for op plans, and commit 47 drops version narration from the collector's comments.

Why and what changed

Sections 1–14 name guard notes (G-…) and backend_gaps as they were when each commit landed. Commit 15 turns each into a commented entry of collector/skips.py with the same scope. Commit 37 turns the six deviation notes into code comments and removes R8.

1. Collector notes replace the evidence mechanism

The change-to-evidence chain (evidence_policy.yaml, evidence_check.py, the evidence waivers) was never wired into CI. This part removes it and gives known measurement gaps one machine-checked home.

  • collector/notes/<backend>.yaml holds two kinds of note, each with a closed schema: a deviation (D-, the table measures something other than what serving runs) and a guard (G-, a case region the collector deliberately does not run). Each records scope, verified_on, sources, retire_via and approved_by; code and data cite the note id.
  • R8 in check_collector_data.py validates every notes file and fails when a note's verified_on is behind the collector pin of a table in its scope, so a pin bump forces a re-check. It replaces feat: upgrade the op collector to vLLM 0.30.0, SGLang 0.5.21 and TRT-LLM 1.3.0rc29 #649's test_standards_exceptions_verified_on.py.
  • The new .claude/rules/collector/overview.md defines the five collector concepts (Pin, Plan, Record, Note, Rule) and the failure principle: every framework plans the same cases, only whole regions are left out at expansion, and guards are reserved for cases that would mismeasure, hang or waste significant GPU time. The other rule files, both collector skills, AGENTS.md, the CodeRabbit instructions and the collector docs follow it.

2, 3 and 6. Op plans: one case list per op for every backend

Each backend's getter used to build its own case list, with its own SM ladder, shape filters and order, so the three tables covered different cases with nothing measured to justify the difference.

  • collector/op_plans.py holds one OpPlan per migrated op: its case type (a named tuple whose string form is the list form case IDs already use), an expand function (every case, in run order, from cases/ YAML), its dtype field and the fields that share one module build. plan(op, backend, sm) removes only the cases that collector/skips.py names. Before commit 15, those were the capabilities.yaml floors and backend_gaps. Each getter is one line, plan_cases("<op>", "<backend>", get_sm_version()).
  • Where the plan picks the kernel or runner a backend measures (the one serving selects, Rule 2), the op declares it in dispatch; --plan-only counts cases per dispatch and --plan-table adds a dispatch column, so each case can be checked against what serving was observed to run.
  • A backend whose run function takes more than the shared case declares rows: the run arguments for one case and its dispatch. Rows say how a case runs, never whether; a case with no row raises.
  • Planning imports no framework, so --plan-only runs on any CPU (tested with SGLang, vLLM, TensorRT-LLM and torch blocked).
  • Commit 15 replaces backend_gaps with collector/skips.py (section 15).
  • tests/unit/collector/test_op_plans.py runs over every registered op: one-line getters, backends that differ only by skip-list entries, and the case string form. Migrating an op needs no new test file.
  • --plan-only --sm N reports each op plan's cases per dtype, the cases left out with the skip entry that matched, and plan_hash; --plan-table DIR writes one CSV row per case.

GEMM. The plan is the gemm_types axis of gemm.yaml times the shared shapes. Removed per-framework filters, each with data already checked in:

Removed Was in Evidence that the cases run
block-scaled n<128 or k<128 skip SGLang, TRT-LLM vLLM 0.30.0 b200: 6,808 of 6,808 such cases with n, k multiples of 16; GPU run below
m*n or m*k >= 2^31 skip TRT-LLM vLLM 0.31.0: 45 of 45; GPU run below
fp8_block n*k >= 2^31 skip TRT-LLM vLLM and SGLang: 222 of 222; GPU run below
fp8_block n%16 / k%16 raise (SM90+) vLLM natural failure; GPU run below
SM120 fp8_block FIXME vLLM 0.30.0 RTX PRO 6000 table has 31,598 fp8_block rows
AIC_COLLECT_GEMM_TYPES, seed-42 shuffle SGLang / all n/a

One backend gap: SGLang fp8_block on SM89/SM120 (G-sglang-gemm-fp8-block-deepgemm-only). SGLang's FIXME(re-verify) serving gaps become deviation notes D-sglang-gemm-bf16-cutedsl, D-sglang-gemm-nvfp4-act-quant and D-sglang-gemm-fp8-block-triton-fallback, re-checked at v0.5.21.

GEMM plan growth per system (cases added; none removed; vLLM unchanged): SGLang +7,918 on SM90/120 and +15,836 on SM100/103; TRT-LLM +45 on SM80, +8,269 on SM89/90 and +16,228 on SM100/103/120.

4. MoE

The three MoE getters read their own quant-mode list, per-model policy and parallel layouts from the same model rows, and planned 7,503 (SGLang), 9,552 (TRT-LLM) and 3,135 (vLLM) layer cases on SM100.

  • One quant-mode axis, common_case_values.moe.quantization_modes, replaces the moe_sglang, moe_trtllm and moe_vllm lists. A model row declares its checkpoint's modes in quantization_modes, the same for every backend; a row that declares none plans every mode. allowed_model_paths keeps int4_wo and the MXFP4 formats to the models that ship them. MoE rows may no longer declare frameworks or per-backend allowed_modes; framework_quantization keeps a backend's module_config and vllm_xpu's own policy only.
  • Dedup and collision checks run once, in the shared expand: rows that time the same layer share one case; two rows that would time different layers under one consumer key raise.
  • Rows: SGLang's add the row's routing and activation and the runner pinned for the SM; TRT-LLM's add min_latency_mode and, for nvfp4 on SM100/103 with at most 256 experts, the min-latency row.
  • Backend gaps, each citing a guard note checked against the pinned source:
Gap Note Why
vLLM, parallel: tp_ep, every SM G-vllm-moe-tp-and-ep FusedMoEParallelConfig.make flattens TP x DP into EP (config.py:1182-1228 @v0.31.0)
vLLM w4a8_mxfp4_mxfp8, SM120 G-vllm-moe-w4a8-mxfp4-sm120 the default MXFP4 backend order never selects MXFP8 activations on SM120 (oracle/mxfp4.py:367-390 @v0.31.0)
SGLang fp8, SM89+ G-sglang-moe-fp8-not-collected the SGLang MoE collector has no per-tensor fp8 path
SGLang int4_wo SM80/86, w4a8_mxfp4_mxfp8 SM120 G-sglang-moe-runner-unpinned no verified serving runner pin for those SMs
TRT-LLM int4_wo, every SM G-trtllm-moe-int4-wo-no-implementation MoE resolution has no W4A16 implementation (moe_resolution.py:659-667 @v1.3.0rc29); replaces the worker raise; every SM after the GPU validation (commit 11)
TRT-LLM fp8_block SM89, w4a16_mxfp4 SM89/120 G-trtllm-moe-cutlass-sm-support on SM89 and SM120 only CutlassFusedMoE is left for these formats (Triton runs on SM90 only, TRTLLM-Gen on the SM100 family only, every other backend falls back to CUTLASS); it runs FP8_BLOCK_SCALES on SM90/SM120 and W4A16_MXFP4 on SM90 only (GPU validation on l40s and rtx pro 6000, commits 11 and 13)
  • Gates deleted because they had no citation at the current pin: SGLang and TRT-LLM fp8_block on SM89, TRT-LLM int4_wo below SM100, TRT-LLM w4a16_mxfp4 on SM89/120. The GPU validation then showed that SGLang runs fp8_block on SM89 and that the three TRT-LLM regions have no implementation, so commit 11 declares those as backend gaps with the cited reason. The TRT-LLM seed-42 shuffle is gone.

MoE cases queued per backend (TRT-LLM counts include min-latency rows):

SM SGLang TRT-LLM vLLM
80 2,166 → 2,049 2,049 → 2,049 828 → 759
89 2,607 → 4,755 3,870 → 3,753 2,223 → 2,265
90 4,989 → 4,755 6,249 → 6,345 2,223 → 2,265
100 / 103 7,503 → 8,274 11,385 → 11,697 3,135 → 3,498
120 6,945 → 7,716 9,339 → 9,537 3,054 → 3,306

Removed classes: bf16/fp8_block on the vLLM-only Nemotron latent width for SGLang and TRT-LLM; per-tensor fp8 on the GLM-5 shape, which no checkpoint ships; TRT-LLM int4_wo on every SM, which never produced a row; vLLM w4a16_mxfp4 below the SM89 floor. WideEP: the vLLM EP population keeps its 46 distinct invocations and drops duplicates under the GLM-5 BF16/NVFP4 names.

5. compute_scale and encoder attention

Both ops already queued the same cases on every backend, through three copies of the same getter. Each now queues exactly the case list it queued before, in the same order, at every SM. compute_scale's shared sweep moves from framework_specific_op_cases.trtllm to all_frameworks_op_cases. Encoder attention's token budget (128K) and 32-bit activation-index budget, applied identically by all three getters, now live once in the expand, which logs how many cases they leave out.

7–10. Linear attention, MLA BMMs, MLA attention, mHC

  • GDN, KDA, Mamba2 and the MLA generation BMMs: each backend queues exactly the case list it queued before, in the same order, at SM80, 86, 89, 90, 100, 103 and 120. GDN's TRT-LLM and vLLM rows drop the SSM state dtype their run functions do not take. vLLM's bf16-only MLA BMM list becomes a backend gap on fp8 (G-vllm-mla-bmm-fp8-not-served: on NVIDIA, vLLM runs both BMMs as bf16 torch.bmm; its fp8/fp4 BMMs are ROCm AITER only).
  • MLA attention (SGLang, TRT-LLM): both collectors built the same catalog per KV dtype and TP; the shared expand deduplicates it once on the loader key at the smallest TP. Rows hold the framework dtype, SGLang's page size and TRT-LLM's KV block size, so Plan.rows builds them on demand; --plan-only on a CPU counts the cases and reports plan_hash: null with the missing module. Case changes: SGLang SM80/86, where the getter raised, become a backend gap (G-sglang-mla-backend-unmapped); SGLang now queues fp8 KV on its triton platforms SM89/120 (+1,540 context, +2,534 generation), whose filter had no citation; TRT-LLM drops fp8 on SM80/86 (−1,540 / −2,534) under the fp8 floor.
  • mHC (SGLang, TRT-LLM, vLLM): a case is one call site of an architecture at one (hidden_size, hc_mult), with its token sweep. Rows keep each backend's run unit and IDs: TRT-LLM and vLLM queue one row per token count, SGLang one row per site that sweeps the token counts in one worker. The runtime architecture gates become backend gaps on the new region field architecture:
Gap Note Cases removed
SGLang, GLM-5.3-Flash G-sglang-mhc-glm5next-not-ported 4, all of which failed the runtime audit
vLLM, DeepSeek-V4 G-vllm-mhc-dsv4-shared-key 140, all of which failed the runtime audit (their rows would share a key with GLM-5.3-Flash's)
TRT-LLM, GLM-5.3-Flash G-trtllm-mhc-glm5next-not-served 0; the getter already folded GLM into the DeepSeek-V4 rows

The getters' --smoke shortcut to 16 tokens is gone; --smoke samples four rows like every other op.

14. Llama 3.1 405B attention shape

Llama 3.1 405B has 128 query and 8 KV heads. At TP1, as under pipeline parallelism, one rank runs all 128 heads. No base grid has that shape: the context grids stop at 96 query heads (64 on vLLM), and vLLM's decode grid stops at 64. The Llama profile now declares the 405B attention row for TP 1, 2, 4 and 8. The TP2–8 shapes deduplicate into the base grids.

The row applies to every backend, like every other attention row. On attention rows, frameworks can only exclude SGLang, and a backend-specific attention row is read by SGLang alone. A TRT-LLM-only row would therefore need a per-backend grid edit.

Backend Context, per SM (SM80/86) Generation
TRT-LLM +357 (+119) unchanged; the decode grid already has 128/8
vLLM +238 (+119) +256 (+128)
SGLang (SM89–120) +357 unchanged

SGLang cases carry the architecture of the profile that owns the shape. The 405B TP1–8 shapes now name LlamaForCausalLM instead of Qwen3MoeForCausalLM or none: 1,071 context and 1,168 generation cases per SM. Only their case IDs change. SGLang's FlashInfer workspace override covers Qwen2, Qwen3, MiMo and Qwen3-VL, and neither old nor new architecture is one of them. The perf row does not record the architecture.

15. One commented list of skipped cases

Every rule that keeps cases out of a collection now lives in one list, collector/skips.py. Each entry has a comment above it with the reason. Entries carry no IDs and no framework versions, and anyone can edit or delete an entry when its reason stops being true.

  • An entry skips a case when every key matches. The keys are backend, op, sm, model (the architecture of the case file a model row comes from), dtype or any case field. A list value matches any of its items.
  • The docstring keeps the bar for adding an entry: the cases would be mismeasured, would hang or would fail as a whole block, or they come from a model the backend cannot serve. Everything else runs and has its failures recorded.
  • The list replaces:
    • the capabilities.yaml floors, now explicit SM lists;
    • its 16 backend_gaps, now 12 entries;
    • the empty denylist.yaml;
    • the 12 G- notes.
      capabilities.py and op-plan region_fields are removed. The notes files keep the six deviation notes, and R8 now accepts only D- notes.
  • plan() applies the list. collect.py and fullnode.py apply it around every getter and before queueing, so ops still on getters see it too. --plan-only prints the matching entry as the reason.
  • A model entry drops that model's rows before deduplication, so a shape that another model also needs is still collected. Model runs (--model-path) drop every op that is skipped for all selected models.
  • New entries: TRT-LLM registers no model class for MiMo, MiMo-V2-Flash, Muse Glimmer or GLM-5.3-Flash, so nothing on TRT-LLM consumes their rows. MiMo-V2-Flash's head_dim 192 also has no TRT-LLM kernel on SM100/103, where the last collection recorded 2,074 to 8,890 such failures per system. Cases removed, all of them TRT-LLM:
TRT-LLM table SM89–120 SM80/86 Removed
attention context −6,324 −2,108 MiMo-V2-Flash head_dim 192, Muse Glimmer window 2048
attention generation −6,042 −3,021 same
GEMM −2,960 (−2,220 on SM89/90) −740 GLM-5.3-Flash's 10 widths at k=4096 that are not in the shared grid
MoE −783 on SM100/103 (SM120 −666, SM90 −450, SM89 −234) −117 MiMo-V2-Flash, GLM-5.3-Flash

hash_closures.yaml now lists skips.py in the shared core instead of capabilities.py, so every module's collector_hash changes.

16. Orphaned measurement child

This is a follow-up to a #649 review comment. If a worker dies between the fork and the child's prctl(PR_SET_PDEATHSIG), the child has already been reparented and never gets the signal. The preexec_fn now captures the worker pid in the parent. After prctl, the child exits when its parent is no longer the worker. Two unit tests cover both cases.

17. SDK: MiMo-V2-Flash and Llama 4 layer types

These came up while checking which models read the attention tables (section 14).

  • MiMo-V2-Flash's hybrid_layer_pattern marks sliding-window layers with 1, and vLLM and SGLang build SWA attention there. The SDK read 1 as global, so it modeled 39 global and 9 SWA layers. It now models 9 global layers (one of them with a dense FFN) and 39 SWA layers.
  • Llama 4 attends globally only in its layers without RoPE: where no_rope_layers is 0, or by default every fourth layer, which is how transformers fills an empty list. The SDK alternated layers, 24/24. It now models 12 global and 36 chunked layers.
  • Golden diff: only mimo-v2-flash-b200-vllm-hybrid-attn-xshape moves, refreshed with pin_goldens.py --refresh.
    • static: ctx −6.1 %, gen +1.0 %, total −4.9 %;
    • agg: TPOT −5.6 %;
    • disagg: TTFT −6.1 %.
    • The Llama-4 Scout case is unchanged: at isl 1024, its 8192-token chunk covers the whole sequence.

18. Attention

There is now one case list for SGLang, TRT-LLM and vLLM.

The shared grid. It is the generic full attention at head_dim 128 that all three backends already collected, plus every model's attention profile on every backend.

  • The other base-grid shapes were an exact key for no packaged model. These were head_dim 64 and 256 grids, window 128, and head counts only one or two backends swept.
  • A model that needs such a shape declares a profile.

Model profiles.

  • A profile carries the model's attention properties: v_head_dim, sinks, and SGLang's runtime window, chunk size, scaling and backend per SM.
  • SGLang's separate variant rows fold into the shared profiles, and frameworks: is retired.
  • Each backend's rows take what its run function supports.

SGLang dispatch.

  • SGLang's attention backend is the plan's dispatch.
  • A shape that two models serve on different backends runs on both. For example, Nemotron-H runs on flashinfer, and the other models and the generic grid run on trtllm_mha.

Skip entries that name a kernel.

  • A skips.py entry may name a dispatch. It then leaves the case out on that kernel only.
  • The SGLang refusals that used to fail at run time become such entries. They logged 8.7k to 13.6k errors per SM89–120 system:
    • BF16-compute prefill on an FP8 KV cache, on fa3 (head_dim ≤ 256) or trtllm_mha;
    • FP8-compute prefill on flashinfer.
  • Also new entries:
    • vLLM's missing FP8-compute input;
    • SGLang on SM80/86;
    • two SM90 TRT-LLM FP8-KV decode crashes.

Gemma 4 on TRT-LLM.

  • TRT-LLM serves Gemma 4 on its default TRTLLM backend (Gemma4ForCausalLM.get_model_defaults), so its rows run there.
  • The FlashInfer route and its guards go, together with the import shims for older releases.
  • kernel_source is not a lookup key for TRT-LLM attention, so the consumer is unaffected.

Decode budget.

  • One decode budget applies to every backend: the largest of the three formulas.
  • The TRT-LLM XQA head-ratio skip on SM100+ is deleted.

Deviations, commented next to the rows.

  • vLLM times MiMo-V2-Flash with V = 192 and gpt-oss without sinks.
  • TRT-LLM adds sinks to every head_dim 64 row, Kimi-K3's draft attention included.

Cases queued per SM. Measured GPU-free against 5aeba48: getter plus queue filter.

SM90 before → after SM100 before → after
SGLang context 56,256 → 29,784 64,008 → 35,326
SGLang generation 45,072 → 36,096 51,550 → 41,370
TRT-LLM context 77,727 → 36,261 73,443 → 36,261
TRT-LLM generation 53,834 → 30,054 52,810 → 30,054
vLLM context 54,706 → 28,390 54,706 → 28,390
vLLM generation 67,892 → 36,096 67,892 → 36,096

Every difference falls into one class:

  • Removed: shapes outside the shared grid; the SGLang refusals above; SGLang trtllm_mha rows for head_dim 256 shapes that only the old SGLang grid planned on that backend.
  • Added: decode points the shared budget admits, and TRT-LLM GQA 40/1 and 48/1 context rows on SM100+.
  • Changed: Gemma 4 rows on TRT-LLM move from FLASHINFER to TRTLLM; some SGLang rows change only their architecture label, which is part of the case ID.

All other rows keep their run arguments and case IDs.

19–20. TRT-LLM module series

TRT-LLM's DSA, MSA and DeepSeek-V4 module workers can now time a list of (seq_len, prefix_len) points in one task (collector/trtllm/module_series.py).

Unchanged:

  • Getters, case IDs and perf rows. A one-point case runs as a series of one.
  • The KV cache manager is still built per point. Its capacity changes what the kernels do: the metadata max_seq_len, the DeepSeek-V4 HCA index-buffer width, and MSA's per-forward index-K pool copy. A manager shared across points would change what the smaller points measure.

What the series saves:

  • The repeated dry runs: one per seq_len, largest point first.
  • The per-task overhead.

Failures:

  • A failure in one point is recorded, and the series continues.
  • A sticky CUDA error ends the series.

Small fix: run_mla_module now releases its KV pool in finally.

Commit 19 adds one line each to the layer-permission and case-authoring rules: a skip entry may name a dispatch kernel.

21. TRT-LLM MLA context labels

TRT-LLM's MLA context attention follows the KV cache dtype. With an FP8 KV cache it runs FP8 FMHA on SM90/100/103/120 (thop/attentionOp.cpp).

  • Bug: the MLA module collector labelled every row mla_dtype=bfloat16. The SDK looks up FP8 checkpoints (DeepSeek-V3/R1, Kimi-K2) with fmha fp8. On TRT-LLM those lookups missed the module table and quietly fell back to op-level ops. fix: correct TRT-LLM MLA labels and GPT-OSS MXFP4 profiles #264 fixed the op-level collector the same way.
  • Collector fix: the module collector now labels these context rows fp8.
  • Shipped tables: the same relabel applies to 51,187 rows in 26 tables.
  • Facts: op_backend_facts.yaml follows. backend_facts.py --check and check_collector_data.py pass.
  • Tests:
    • The large-EP task and the wide-EP compile-engine parity case now use fp8 FMHA with the FP8 KV cache.
    • The wide-EP golden is unchanged, because it reads the same rows, relabelled.

22. DeepSeek-V4 KV pool on TRT-LLM Blackwell

The TRT-LLM DeepSeek-V4 collector timed a BF16 KV pool on SM100/103. The probe behind that choice tried only the packed fp8_ds_mla pool, which is not servable off Hopper.

  • Blackwell serves the plain FP8 pool. It is TRT-LLM's recommended DeepSeek-V4 configuration and what its B200 CI runs.
  • The SDK already looks DeepSeek-V4 up with an FP8 KV cache, so the BF16 rows were never read by default.
  • The collector now times FP8 KV on every SM.
  • The GPU run below checks that it boots on gb200. Re-collecting the Blackwell DeepSeek-V4 tables follows with the module ops' shared grid.

23. TRT-LLM MLA FMHA on an FP8 KV cache

TRT-LLM has no separate precision for MLA attention. On SM90/100/103/107/120, an FP8 KV cache turns on FP8 context MLA (mFP8ContextMLA in thop/attentionOp.cpp). Generation MLA runs FP8 for an FP8 KV cache on every SM. So TRT-LLM cannot run BF16 compute on an FP8 KV cache on those SMs. Since commit 21 the tables say so, and an explicit fmha=bfloat16 on an FP8 KV cache finds no rows.

  • helpers.resolve_trtllm_mla_compute resolves such a request to fp8 and logs a warning. It covers DeepSeek-V3/R1 and Kimi-K2/K2.5/K3.
  • It sits next to the SGLang MLA resolver and runs at the same call sites. The Task resolves it before validation.
  • An unset FMHA keeps the checkpoint inference, which already picks fp8 for an FP8 KV cache.
  • These are unaffected:
    • a BF16 KV cache;
    • SM80/89, where TRT-LLM computes BF16 context MLA on an FP8 KV cache;
    • other backends;
    • whole-model FPM.

24. Step-3.7-Flash configuration

The packaged Step-3.7-Flash configs were normalized by hand when the model was added (AIConfigurator #1447 and #1452). They dropped two upstream values:

  • attention_other_setting. It gives the 33 sliding-attention layers 96 query heads (8 KV groups, head_dim 128), against 64 heads in the global layers.
  • The context length. The packaged files said 65,536; every upstream revision says 262,144.

The SDK therefore sized every sliding layer with 64 heads, while the collector plans the 96-head shapes that vLLM, SGLang and TRT-LLM build.

Both files now carry the upstream values at pinned revisions: stepfun-ai/Step-3.7-Flash@5f62440 and -FP8@b3d7916. THIRD_PARTY_NOTICES.md, in both copies, records the files, revisions, the Apache-2.0 license and every normalization.

25–26. Batch rows and the MLA attention modules

OpPlan.batch_rows[backend] lets one task time several cases. It returns pairs of (row, cases it times), and plan() checks that every kept case is timed by exactly one row. Dropped, doubled or foreign cases raise. Rows carry their points explicitly and never re-derive them.

mla_context_module and mla_generation_module move onto op plans for TRT-LLM and vLLM. SGLang has no registered MLA module op.

Heads. The heads are the union over the MLA models: DeepSeek-V3's 128…1 plus Kimi-K3's 96/48/24/12. This is the only point difference. TRT-LLM gains those four heads, +50% points on every SM, on its own precision combos. No point is lost.

Precision. The precision combos per backend and SM are unchanged (M2).

  • vLLM's FP8-compute context cases on SM120/121 stay queued here. Commit 35 skips them: vLLM falls back to BF16 there, and the collector refuses to record that as FP8.

Rows.

  • TRT-LLM queues one run_mla_module_series_worker task per (model, heads, precision, batch), with explicit [seq_len, prefix_len] points.
  • vLLM rows are identical to before.

Memory. vLLM generation's KV-fit filter becomes a refusal at run time, before allocation (does not fit …). The points refused on each device are exactly the ones the filter used to drop: 72 on H100-80GB, 36 on A100-80GB, 72 on L40S, none on H200, B200, B300 or RTX PRO 6000. H100 and H200 now get the same plan.

Side effects.

  • --plan-only sends planning output to stderr, so the module loader's notes no longer break its JSON.
  • TRT-LLM MLA module case IDs change, so a --resume from an older checkpoint re-runs them.

27–28. DSA modules and SGLang's skip-indexer variants

dsa_context_module and dsa_generation_module move onto op plans for all three backends. SGLang's *_skip_indexer variants follow.

Grid (one rule for every backend).

  • A context point (b, s, p) qualifies when b·s ≤ 16,384, b·(s+p) ≤ 1,048,576, and p+s fits the model's context.
  • Prefixes use 13 anchors at ×2 spacing. 2,048, the top-k knee, is kept.
  • At prefixes of 16K and above, only chunk-sized isl is planned.
  • Decode adds KV lengths of 256K, 512K and 1M at b ≤ 4, where the model reaches them.

Models. One rule for every backend: the longest-context checkpoint per (architecture, operator GEMM). That gives DeepSeek-V3.2, GLM-5.2, GLM-5.2-FP8, and DeepSeek-V3.2-NVFP4 on SM100+.

Precision. Each backend keeps exactly its current precision combos. New skips.py entries record why:

  • TRT-LLM has no FP8 KV on SM90;
  • SGLang builds the checkpoint's own projection GEMM;
  • SGLang skips one-token prefills, which crash the task.

Device. The plan no longer depends on the device. SGLang's prefill-chunk and KV-pool checks and vLLM's generation memory filter now refuse points at run time, before allocation.

Rows.

  • TRT-LLM runs series through run_mla_module_series_worker.
  • vLLM rows carry explicit [isl, prefix] points.
  • SGLang rows carry their [batch, isl, prefix] points into the subprocess, which no longer derives its own sweep. Its FP8-KV attention backend is the plan's dispatch.
  • AIC_DSA_CONTEXT_* is gone.

Context points per SM, before → after.

TRT-LLM vLLM SGLang
SM90 4,880 → 11,110 71,660 → 22,220 57,344 → 14,590
SM100/103/120 14,640 → 33,330 107,490 → 33,330 57,344 → 14,590

Why the counts move:

  • TRT-LLM gains prefixes up to 1M, where it previously stopped at a prefix of 128.
  • vLLM and SGLang lose off-anchor prefixes and non-chunk isl at long prefixes.
  • Decode changes by a few hundred points per backend.
  • Case IDs change: rows now carry their point lists.

29. MSA modules

msa_context_module and msa_generation_module move onto op plans for all three backends. The model is still MiniMax-M3 (1M context) at its TP 1–16 head shards, 64 to 4 heads.

Grid. The DSA rule from section 27:

  • b·s ≤ 16,384 new tokens and b·(s+p) ≤ 1,048,576 KV tokens;
  • the 13 prefix anchors, keeping 2,048, which is M3's top-k knee too (16 blocks of 128);
  • from a 16K prefix on, only chunk-sized isl;
  • decode adds KV lengths of 256K, 512K and 1M at b ≤ 4.

Precision. Each backend keeps exactly its current precision combos. New skips.py entries record why:

  • TRT-LLM takes no FP8 KV cache for MSA;
  • SGLang times BF16 projections only, and no FP8 KV on SM89.

vLLM's FP8 KV on SM80/86 now falls under the existing FP8 floor; no MSA table exists there.

TRT-LLM off SM100/103. On SM80–90 and SM120, TRT-LLM runs MSA on its Triton reference, a dense FP32 attention. A skips.py entry stops its context at a 32K prefix there, which replaces the 32,768-token Triton budget:

  • up to that prefix, a 16K chunk does no more work than the isl-32768 point collected before (18 s per forward on H200);
  • a longer prefix runs out of memory.

Device. As for DSA, the plan no longer depends on the device. Points are refused at run time, before allocation:

  • SGLang refuses points beyond the prefill chunk it uses on the device, or beyond its KV pool. The collector no longer raises the chunk to the largest planned shape.
  • vLLM refuses decode points whose KV cache cannot fit.

SGLang's plan-time memory filter, get_msa_head_counts and the AIC_MSA_* switches are gone.

Rows.

  • TRT-LLM: one run_msa_module_series_worker task per module build.
  • vLLM: one task per point, as before.
  • SGLang: rows carry their [batch, isl, prefix] points instead of a manifest the getter built.

Points per SM, before → after.

TRT-LLM vLLM SGLang
context, SM90 1,940 → 5,210 4,880 → 11,260 35,000 → 5,630
context, SM100 3,660 → 8,445 7,320 → 16,890 35,000 → 5,630
decode, SM90 1,840 → 1,930 3,680 → 3,860 1,850 → 1,930
decode, SM100 2,760 → 2,895 5,520 → 5,790 1,850 → 1,930

Why the counts move:

  • TRT-LLM and vLLM lose prefix 128 and b·s beyond the 16K chunk. They gain prefixes up to 1M; TRT-LLM stops at 32K on SM80–90 and SM120.
  • SGLang loses the one-shot prefills beyond the chunk, its 26-value prefix list, batches 512 and 1,024, isl 4 and 8, and decode KV 1. It gains isl 1,536 to 12,288 and the long-KV decode points.

30. Refusals under torch.distributed

The DSA GPU diag on H200 refused 22 SGLang prefills as planned: more new tokens than the 8,192 SGLang chunks on that GPU. They were recorded as unexpected rather than kernel_limit. Under torch.distributed, SGLang's subprocess prefixes every output line with [rank0]: , so the parent found no traceback to read the classified error from. The DSA/MLA and MSA module runners now strip that prefix first.

31–32. DeepSeek-V4 attention modules and sparse kernels

The CSA and HCA context and generation modules move onto op plans for all three backends. dsv4_paged_mqa_logits_module (vLLM, SGLang) and SGLang's dsv4_csa_topk_calib follow.

Grid. The long-context rule from section 27 applies, with DeepSeek-V4's top-k knees kept as anchors: 512 for Flash and 1,024 for Pro. Decode reaches 1M KV on every backend.

Precision. Each backend keeps its combinations on every SM.

  • TRT-LLM and vLLM time fp8_block projections; SGLang times BF16 and fp8_block.
  • The KV cache is FP8 everywhere, as a row choice. On TRT-LLM it is the fp8_ds_mla pool on SM90 and the plain FP8 pool elsewhere (section 22).
  • Two new skips.py entries:
    • TRT-LLM and vLLM take no BF16 projections, which keeps the coverage collected before;
    • SGLang's native deepseek-ai/* checkpoints take no fp8_block on SM90, replacing a device check.

Device. SGLang now refuses points with a classified reason instead of skipping them silently:

  • beyond the device's prefill chunk, before the ModelRunner is built;
  • beyond the full or sliding-window KV pool, after it is built.

The context hidden-state filter is gone: at 16,384 new tokens the BF16 hidden state stays under 250 MB, so it never triggered. The refusal helpers move to sglang/runtime_limits.py, which MSA shares.

Rows.

  • TRT-LLM: one run_dsv4_attn_series_worker task per module build, so 72 context and 88 decode tasks instead of 5,840 and 1,544.
  • vLLM: one task per point, as before; its case IDs are unchanged.
  • SGLang: every row carries its (prefix, seq_lens) points; the subprocess no longer derives a grid.
  • topk_calib takes its points from the CSA plan.
  • paged_mqa_logits keeps vLLM's 3,918-point grid, which SGLang now shares.
  • Gone: AIS_DSV4_KV_CACHE_DTYPE, AIC_VLLM_DSV4_MAX_SEQ_LEN, AIC_VLLM_DSV4_SPARSE_MAX_QUERY_TOKENS and the --smoke checks.

Points per kind, before → after (SM90 to SM120; SM80–89 plan none):

TRT-LLM vLLM SGLang
context 5,840 → 4,504 5,840 → 4,504 113,872 → 9,008
decode 1,544 → 1,600 1,544 → 1,600 3,200 → 3,200
paged_mqa_logits – 3,918 → 3,918 4,110 (SM90/120), 4,790 (SM100/103) → 3,918
topk_calib – – 2,251 (SM90/120), 2,591 (SM100/103) → 763

Why the counts move:

  • TRT-LLM and vLLM context lose isl 4 and 8, batches 512 and 1,024, b·s above 16,384 and off-anchor prefixes. They gain the anchors up to 1M. Decode gains 56 points above 65,536 KV.
  • SGLang context loses:
    • 66,176 one-shot prefills beyond the chunk;
    • 18,368 off-anchor prefixes;
    • 9,440 points above 1M KV;
    • 3,136 non-chunk isl at 16K+ prefixes;
    • isl 4 and 8, and batches 512 and 1,024.
  • SGLang paged_mqa_logits loses past KV 10,000 and 1,000,000 and 8 decode shapes at exactly 1M. On SM100/103 it also loses 680 prefills above 8,192 new tokens.
  • topk_calib loses the same classes as SGLang context. On SM90/120 it gains 96 prefills of 8K–16K new tokens, which H200's CSA module refuses, so they go unused there.
  • Case IDs: TRT-LLM rows become series lists, and SGLang rows gain point lists.

33–34. Wide-EP MoE and DeepEP

moe_ep (the SGLang and TRT-LLM wide-EP registries), deepep_ll and deepep_normal move onto op plans.

moe_ep cases. One case per large-EP build, from the wideep: true MoE rows: quant mode, routed-expert layer, EP size and EPLB layout.

  • Builds that write the same table key are planned once (commit 34). The owning model is the first checkpoint that plans that quant mode.
  • skips.py keeps each backend's builds:
    • SGLang times fp8_block only, without EPLB, on the checkpoint's own quant mode;
    • TRT-LLM's fp8_block on SM89 fails in the DeepGEMM compiler.

Rows.

  • TRT-LLM rows keep their 16 run arguments.
  • SGLang rows keep their 9, and SGLang still sweeps tokens and distributions inside the task.
  • AIC_ACCURATE_WIDEEP_SIM is gone. Rows always carry the accurate simulation, which was already the default.

deepep. One case per distinct (hidden, experts, top-k) of the packaged model configs, 25 shapes as before.

moe_a2a stays on its own population. Each backend's collector measures a different all-to-all library for one launched world (EP size and node count), with backend-specific axes. There is no shared case list to plan.

Builds per SM, before → after.

SM80/86 SM89 SM90 SM100/103/120
moe_ep SGLang 62 → 0 62 → 46 62 → 46 62 → 46
moe_ep TRT-LLM 0 0 224 448
deepep_ll / deepep_normal 25 25 25 25

Why the counts move:

  • SGLang on SM80/86 falls under the shared FP8 floor. FP8 needs SM89 and DeepGEMM needs SM90, so those cases could not run.
  • SGLang drops 16 duplicate builds per SM: GLM-5.1-FP8 and GLM-5.2-FP8 at eight EP sizes write the same keys as GLM-5-FP8.
  • TRT-LLM measures the same builds. Within a model, quant mode and EP size they now run layout before draw. Some rows carry a different owning model; the runner ignores the model and the table has no model column, but a --resume from an older checkpoint reruns those rows.

35. vLLM FP8-compute MLA context outside the SM100 family

vLLM takes FP8 prefill queries only on the SM100 family (backend_supports_prefill_query_quantization in mla_attention.py) and falls back to BF16 elsewhere.

  • The collector refuses to record that fallback as FP8, so these cases failed on every SM120 run. The shipped SM120 tables hold no FP8-compute rows.
  • The skip entry now covers SM120: vLLM mla_context_module on SM120 goes from 972 to 648 cases.
  • The SDK picks the context FMHA from the data it finds, so nothing changes there.

36. One collector hash

Before this commit:

  • each module's collector_hash covered a closure of files listed in hash_closures.yaml;
  • CI turned the per-module hashes into a changed-operations manifest.

Nothing has read that manifest since commit 1, and a missed closure entry silently weakens the hash: MSA's closure lacked op_plans.py.

Now. collector_hash is one hash over:

  • the collector source: .py, .yaml, .yml, .sh and .patch files under collector/;
  • the SDK Python files and packaged model configs that some runners import.

JSON is left out, because the TRT-LLM MoE autotuners write their caches there.

Deleted: hash_closures.yaml, the closure loader, tools/perf_database/changed_ops.py and its CI steps.

Unchanged: collector_ref, case_plan_hash and the collection_meta.yaml schema.

37. Deviation notes as code comments

The six D- notes move into comments at the code that deviates:

Note Comment location
the three SGLang GEMM notes sglang/collect_gemm.py
TRT-LLM mHC trtllm/collect_mhc_module.py
vLLM compute_scale vllm/collect_computescale.py
vLLM fp8_static vllm/collect_gemm.py

Each comment keeps what differs, the direction of the error, the sources and how to retire it.

collector/notes/, its schema and R8 are deleted, so check_collector_data.py is back to R1–R7. The four mHC reuse.yaml files point at the code comment.

The cost: a framework pin bump no longer forces a re-check of these six deviations.

38–39. GLM-5 sparse kernels and DeepSeek-V4.1

GLM-5 sparse kernels. glm5_mqa_logits_module, glm5_topk_module and glm5_dsa_attn_module (SGLang) take the DSA module plan's points for their checkpoint, on the shared long-context grid.

  • One task per (model, batch) carries its [prefix, isl] points. The worker no longer derives its own, and its --smoke sampling is gone.
  • Two commented skips.py entries keep today's model choice: GLM-5 on Hopper, GLM-5.2-NVFP4 on SM100/103/120.
  • dsv32_dsa_sparse_modules.py is deleted. It was unregistered, with no table and no consumer.

Points go from 3,026 to 672 on SM90 and from 3,333 to 692 on SM100/103/120, still in 11 tasks per SM.

  • Lost: off-anchor prefixes (including the max-position ceilings), b·s above 16,384, b·(s+p) above 1M, context batches 512 and 1,024, isl 4 and 8, non-chunk isl at 16K+ prefixes, decode KV 1 and the old decode ceilings.
  • Gained: isl 1,536 to 12,288, long-KV decode, and a one-token prefill per batch.

DeepSeek-V4.1. dsv41_module's existing {id, params} rows become the plan's rows.

  • The plan is unchanged: 8 rows on every SM.
  • DSV41_EXECUTION_PROFILE is gone. To run one profile, use --case-filter dsv41_full_ or --case-filter dsv41_decoder_bounded_.

40. DeepSeek-V4 top-k calibration through skips.py

The calibration plan used special plan code, with a log line, to drop DeepSeek-V4-Pro. Now one skips.py entry leaves sgl-project/DeepSeek-V4-Pro-FP8 out, and the default rows are unchanged (11 per SM on SM90–120).

Without Pro's table, the SDK applies no top-k correction to Pro, and its CSA context-parallel model refuses to run. Commit 45 removes the entry again.

41. XPU attention capacity at run time

The vLLM XPU attention getters dropped cases by a generation-time memory budget; this was the last plan-time memory filter.

  • run_attention_torch now refuses a KV pool the device cannot hold before allocating it (FIXME(kernel-limit): does not fit …).
  • The getters return 14,294 context and 24,532 generation cases on any device. Before, they returned 13,946 / 24,044 on 24 GB parts and 14,120 / 24,300 on 48 GB parts.
  • The rules' "one sanctioned filter" section becomes "Capacity: refuse at run time, before allocation".

The XPU getters themselves stay outside op plans: XPU has no SM and keeps its own case lists (Follow-ups).

42. Static guards into skips.py

A run-time FIXME(kernel-limit) raise whose condition depends only on case fields and the SM fails on every run. The plan no longer queues those cases, and skips.py entries carry the reasons.

Guard Ops SMs Cases before → after
TRT-LLM MLA below Hopper mla_context / mla_generation 80, 86 1,540 / 2,534 → 0
89 3,080 / 5,068 → 0
MLA modules (context / generation) 80, 86 108 / 132 → 0
89 432 / 528 → 0
TRT-LLM MLA decode, more than 16 heads per rank and not a multiple of 16 mla_generation 100, 103 5,068 → 4,706
TRT-LLM and vLLM DSA, fewer than 8 heads per rank DSA context / generation 100, 103 540 / 660 → 486 / 594
vLLM DSA with FP8 KV, more than 64 and fewer than 128 heads DSA context / generation 100, 103 none planned today
TRT-LLM wide-EP fp8_block off Hopper moe_ep 100, 103, 120 448 → 224

These guards stay at run time:

  • the per-shape MoE alignment checks, which fail single shapes;
  • SGLang's trtllm_mla decode head range, which depends on the image's CUDA version;
  • vLLM's MiniMax-M3 decode-scorer check, which no planned head count reaches;
  • TRT-LLM DSA on SM120, until commit 44 settled it with a GPU run.

43. CI contract tests

Commit 36 removed Collector Data's comparison-base fetch and the base_sha input of collector-check.yml, together with the changed-ops manifest they served. Three tests in tests/test_ci_workflow_contracts.py still asserted them, so Repository Policy failed.

  • The safety-fix test keeps its --depth=1 check and drops the BASE_SHA fetch check.
  • The comparison-base helper checks only prediction-regression, the remaining consumer of verify-target's comparison base.
  • The full-history fetch test is removed with the fetch it guarded.

44. TRT-LLM sparse MLA on SM120

A GPU run decided the SM120 guard of the TRT-LLM DSA modules (rtx pro 6000, TRT-LLM 1.3.0rc29, pipeline 78388970). With the raise removed, all 109 points of the seven selected tasks failed. The tasks covered DeepSeek-V3.2 and GLM-5.2, BF16 and FP8 KV, context and generation. Each point failed with TrtllmAttention's "DeepSeek-V4/DSA sparse MLA on SM120/SM121 requires kv_cache_config.dtype='fp8_ds_mla'". The DeepGEMM assert that the raise cited no longer fires.

  • The same check covers DeepSeek-V4. Its attention inherits MLA, and collect_dsv4_attn builds the fp8_ds_mla pool on SM90 only.
  • One skips.py entry leaves out TRT-LLM's DSA modules and the four DeepSeek-V4 attention modules on SM120, and the raise goes.
  • Queued on TRT-LLM SM120 (series rows / points):
    • DSA context 540 / 33,330 → 0, and generation 660 / 11,310 → 0;
    • DeepSeek-V4 CSA and HCA context 72 / 4,504 each → 0, and generation 88 / 1,600 each → 0.
  • No shipped table has TRT-LLM rows for these ops on SM120. Timing them on the fp8_ds_mla pool, as serving does, is a follow-up.

45. DeepSeek-V4-Pro top-k calibration

Commit 45 removes the skips.py entry of commit 40, so the default SGLang plans calibrate sgl-project/DeepSeek-V4-Pro-FP8 next to Flash.

  • Queued on SM90–120: 22 tasks and 1,526 points per SM instead of 11 and 763; nothing below SM90.
  • Pro rows key 128 native heads and Flash rows 64, so the SDK applies each table only to its own geometry.
  • The collection itself runs separately. Until Pro's table ships, Pro predictions keep running without the top-k correction.
  • GPU smoke, SGLang 0.5.21, one GPU per run: pipelines 78444833 (h200) and 78444839 (b200), Pro's batch-1 and batch-1,024 tasks (168 points).
    • Both GPUs time every point: 336 rows each (two score modes), no failures. Prefixes reach 1,048,575 tokens and prefills 16,384.
    • Rows are zero exactly where the compressed KV fits within Pro's top-k of 1,024 entries; every larger point is timed.
    • At the points Flash's tables share, Pro's non-zero values match Flash's: median ratio 1.01 on h200 and 1.00 on b200.

46. Collector rules

The rules, skills and review instructions describe the collector as it now is: every op except moe_a2a is on an op plan, and collector/skips.py is the one place that leaves cases out.

  • overview.md: the collector in five things (Pin, Plan, Record, Note, Rule), the failure principle, the rules and their checkers, and an open-gaps table (moe_a2a, the vLLM XPU getters, Record counts, the consumer signal).
  • layer_permissions.md: one "Leaving cases out" section holds the four reasons a case may be skipped. The capacity, kernel-selection, serving-parity, deviation, version and module-boundary rules keep their substance, and the version contract lists every re-check a pin bump needs.
  • failure_handling.md and case_authoring.md point to those sections instead of restating them.
  • The op-development, auto-collect and silicon-alignment skills, AGENTS.md and both CodeRabbit configurations no longer mention quant-axis min_sm floors, in-collector skips, capability floors or generation-time memory filters.

47. Version narration

Comments, docstrings and messages under collector/, tools/perf_database/ and the collector tests, rules and skills state current facts instead of history. There are no behaviour changes.

  • Source citations name the file and symbol, or file:line, without a framework version.
  • Decision dates, internal PR or issue numbers and "re-verify on the next bump" sentences are gone. The version contract in layer_permissions.md makes every pin bump re-check citations, skips and notes.
  • Kept on purpose:
    • functional version strings;
    • the manifest's retired-pin notes;
    • the source fields of kernel_source_backends.yaml;
    • upstream framework PR citations;
    • FIXME(re-verify) comments.
  • Left for a follow-up: 62 comment lines in 25 collector files still date a source audit or a hardware observation. Several explain how older shipped tables were collected.

Review map

  • Risk level: medium. Part 1 changes no collector behaviour. The op plans change what the backends queue for GEMM, MoE, MLA attention and mHC; the other migrated ops queue exactly what they did. Commit 14 adds the Llama 3.1 405B TP1 attention cases. Commit 15 removes TRT-LLM cases of four models TRT-LLM cannot serve. Commit 17 changes SDK predictions for MiMo-V2-Flash and Llama 4. Commit 18 shrinks the attention case lists to the shared grid (section 18). Commit 44 removes the TRT-LLM DSA and DeepSeek-V4 module cases on SM120, which failed on every run. Commit 45 adds DeepSeek-V4-Pro's top-k calibration to the SGLang plans on SM90–120 (11 tasks, 763 points per SM).
  • Public or serialized contract changed: yes.
    • New: collector/op_plans.py, collector/skips.py, --plan-table, a plans block in --plan-only JSON.
    • collector/skips.py replaces capabilities.yaml (floors and backend_gaps), denylist.yaml and the G- notes.
    • Removed by commits 36–37: collector/notes/ with R8, hash_closures.yaml, and tools/perf_database/changed_ops.py with its CI step. collector_hash keeps its field but is one hash over the collector source.
    • cases/base_ops/moe.yaml moves to one quant-mode axis; MoE model rows use quantization_modes.
    • evidence_policy.yaml and evidence_exceptions.yaml are removed.
    • Case IDs keep their list form; the IDs of every case queued before are unchanged except as noted for MoE. A MoE case the old getters queued keeps its run arguments; for a few hundred deduplicated cases only the owning model name changes, to another row that times the same layer, so a --resume from an older checkpoint runs those cases again.
  • Compatibility or rollback concern: shipped tables are untouched (the four mhc/trtllm/1.3.0rc23/reuse.yaml files only change their reason comment). A re-collection adds or drops the cases above. Rollback is a revert.

Evidence

  • pytest tests/unit/collector tests/unit/tools at b287241: 4,624 passed, 32 skipped, 0 failed (12 min 48 s).
  • Same suites at 3fde5a9ac, commit 6 before its plan-table column: 4,659 passed, 31 skipped, 0 failed (13 min 37 s); test_op_plans, the MoE population, dedup, model-case, capabilities and provenance tests at b012f30: 316 passed.
  • Same suites at b848721: 4,774 passed, 32 skipped, 2 failed (13 min 46 s). The two failures were TRT-LLM mHC getter tests that did not pin the SM under a stub torch; with the SM pinned, they and the other mHC tests pass.
  • At 57a3da8 (notes only): check_collector_data.py R1–R8 OK; test_check_collector_data and test_op_plans 239 passed.
  • At 88c0f92: pytest tests/unit/collector tests/unit/tools 4,776 passed, 31 skipped, 1 failed (12 min 32 s); the failure was the SGLang SM90 structural population count, updated in the commit; the attention, case-generation, profile and op-plan tests then pass (669). The attention getters were dumped GPU-free before and after for the three backends at SM80–120: the added cases are exactly the counts in section 14, and no case is removed.
  • check_collector_data.py: R1–R8 OK. ruff@0.14.1 check and format --check are clean; the documentation-link check passes.
  • GPU-free parity:
    • GEMM, compute_scale, encoder attention, GDN, KDA, Mamba2 and the MLA BMMs: the op plans queue the same case lists as the getters they replace, in the same order, for SGLang, TRT-LLM and vLLM at SM80, 86, 89, 90, 100, 103 and 120.
    • MLA attention and mHC: every case queued before and kept has identical run arguments; the added and removed cases are exactly those listed above.
    • MoE: the old and new getters were run with the frameworks stubbed at the same seven SMs. Every case present in both has identical run arguments, apart from the owning model name of a few hundred deduplicated cases; the added and removed cases are exactly the classes listed above.
  • GEMM GPU validation at b4787fc (same GEMM case list as the head): 6 single-GPU runs on the internal collection harness, pipelines 78103835 to 78103852, SGLang 0.5.21, TRT-LLM 1.3.0rc29 and vLLM 0.31.0 on h200_sxm and gb200, one exact-case plan per run.
Case class SGLang h200 · gb200 TRT-LLM h200 · gb200 vLLM h200 · gb200
controls, 10 per dtype 30/30 · 40/40 30/30 · 40/40 30/30 · 40/40
block-scaled, n or k < 128 (fp8_block) 24/36 · 24/36 16/36 · 32/36 24/36 · 24/36
block-scaled, n or k < 128 (nvfp4) – · 4/36 – · 16/36 – · 36/36
m*n or m*k >= 2^31 – 12/12 · 16/16 –
fp8_block n*k >= 2^31 – 6/6 · 6/6 –
unaligned fp8 / fp8_block – 0/12 · 0/12 0/3 · 0/3
  • Every failure is a recorded exception: SGLang's own NVFP4 alignment check and DeepGEMM asserts, TRT-LLM NVRTC compilation and n%16/n%32 checks, vLLM assertions.
  • Control latency against the checked-in tables: median −1.3 / −0.5 / −1.6 % on h200 and −3.3 / 0.0 / −1.4 % on gb200 (SGLang / TRT-LLM / vLLM). Single shapes vary by up to −34 % / +18 %, with the same spread for vLLM, whose GEMM code did not change.
  • MoE GPU validation at b012f30: exact-case plans for the newly queued classes plus controls, one GPU per run, on the internal collection harness (plan v4moe_20261031b).
    • SGLang 0.5.21: l40s and gb200 pass every new case, including SM89 fp8_block (3/3) and nvfp4 on undeclared rows (3/3). Controls against the 0.5.21 tables: 0.0 % and −0.1 %.
    • vLLM 0.31.0: h200 passes every case. gb200 passes fp8, fp8_block, bf16 and 2/3 nvfp4, with controls at −0.5 % (h200) and −1.0 % (gb200) against the 0.30.0 tables. It fails fast on two collector-side limits: w4a8_mxfp4_mxfp8 runs only for the DeepSeek-V4 fp4-expert checkpoints (gpt-oss, Kimi-K3 raise ValueError), and the NVFP4 Qwen3.8 alias has no packaged config.
    • TRT-LLM 1.3.0rc29: h200 and gb200 pass fp8_block (6/6 each) and gb200 passes nvfp4 (3/3); w4a16_mxfp4 passes 2/3 on both, the third being an out-of-memory shape. int4_wo fails on a100, l40s and h200, and fp8_block and w4a16_mxfp4 fail on l40s, all at layer build with "no MoE implementation can serve this layer" (commit 11). Controls against the rc29 tables: −0.2 % (h200), +0.1 % (gb200); the l40s bf16 controls are +12 % on every token count with an unchanged kernel, most likely a slower node.
    • TRT-LLM 1.3.0rc29 on rtx pro 6000 (SM120): int4_wo and every w4a16_mxfp4 case fail at layer build the same way (commit 13). fp8_block passes 5/6; the sixth is GLM-5.3-Flash's 288 experts, which SM120's Triton fp8_block kernel rejects at compile time because it needs a power-of-2 expert count, a kernel limit the collector already records. nvfp4 3/3, bf16 1/1; w4a8_mxfp4_mxfp8 runs out of memory on the Kimi-K3 shape. Controls against the rc29 table: median 0.0 % over 162 token counts (−10.9 % to +5.5 %).
  • MLA GPU validation at 41777a4 (plan v4mla_20261031b): SGLang 0.5.21 on l40s runs the newly queued fp8-KV cases (3 context, 3 generation, all pass); bf16 controls −3.4 % / −3.6 % (context) and −0.1 % / −0.3 % (generation) against the 0.5.21 tables. rtx pro 6000 passes all ten as well (6 fp8-KV, 4 bf16 controls); controls +3.3 % / −0.6 % (context) and +1.0 % / +4.7 % (generation). On both GPUs fp8-KV context is slower than bf16 (2.0× on l40s, 3.8× on rtx pro 6000) and fp8-KV generation faster (−28 % to −43 %).
  • At 5aeba48:
    • pytest tests/unit/collector tests/unit/tools: 4,758 passed, 31 skipped, 0 failed (13 min 30 s).
    • pytest tests/unit/sdk: 5,368 passed, 13 skipped, 1 failed. The failure is test_fpm_learned::test_feature_list_is_unique_and_bounded, which needs scikit-learn and is not installed here; it fails the same way without this PR.
    • test_engine_step_parity.py and test_compile_engine_parity.py: 389 passed, after the MiMo refresh in commit 17.
    • ruff@0.14.1 check and format --check on python/aisimulate and tests: clean.
  • Commit 15, GPU-free parity, against 88c0f92:
    • Scope: every op plan and both attention getters, for the three backends at SM80–120, comparing getter rows and the rows left after the queue filter.
    • Result: identical, except for the TRT-LLM removals listed in section 15, with order kept.
  • At 4622201:
    • pytest tests/unit/collector tests/unit/tools: 4,774 passed, 32 skipped, 0 failed (13 min 30 s).
    • ruff: clean.
    • --plan-only and --plan-table: checked for SGLang on SM100.
    • Before/after attention dumps: SGLang, TRT-LLM and vLLM at SM80–120, plus XPU. Every difference falls into a class from section 18.
  • At d6a95a4:
    • collector and tools suites: 4,801 passed. The one failure, a pinned b200 table hash, is updated in commit 21.
    • SDK suite: 5,367 passed. The large-EP task test is updated in commit 21. test_fpm_learned needs scikit-learn, which is not installed here.
    • parity: 389 passed.
    • ruff, backend_facts.py --check and check_collector_data.py: clean.
  • At 1a2ef69:
    • pytest tests/unit: 13,913 passed. The one failure, test_fpm_learned, needs scikit-learn.
    • parity: 389 passed.
    • check_packaged_legal_files.py: passes.
  • GPU A/B for commit 20. One GPU per run; 120 points per system.
    • Old side at 5aeba48: pipelines 78317988 (h200) and 78317989 (gb200). New side at d6a95a4: 78325801 and 78322566.
    • All 240 new-side points pass.
    • DSA, MSA and DeepSeek-V4 generation on h200: medians within ±0.3%, every point within ±2%. On gb200, DSA and MSA medians are within ±1% (worst MSA point −5.7%).
    • gb200 DeepSeek-V4 now runs the FP8 KV pool of commit 22. It boots on all 64 points: context −12.5 % / −15.2 % and generation −6.6 % / −7.9 % against the BF16 pool.
    • h200 DeepSeek-V4 context: the first old-side run was slow. A rerun of the old revision (78331251) moves by the same amount (CSA −32 % median) and matches the new side within +0.2 % / +0.1 % (max +2.5 %). So commit 20 changes nothing; TRT-LLM DeepSeek-V4 context timings can vary by about 1.5× between runs.
  • At d47193d:
    • collector and tools suites: 4,839 passed.
    • GPU-free MLA module parity against 1a2ef69: the only differences are the classes in section 26.
  • At 8de87d6:
    • collector and tools suites: 4,896 passed.
    • DSA GPU-free parity against d47193d: every difference is classified as in section 27, and the precision combos are unchanged per backend and SM.
    • DSA GPU diag, one GPU per run, pipelines 78354995–78355014. It covers the new checkpoints, long prefixes, long-KV decode and the run-time refusals.
Rows measured TRT-LLM b200 · h200 vLLM b200 · h200 SGLang b200 · h200
context 510 · 255 388 · 266 232 · 90
decode 20 · – 17 · – 184 · 184
failures 0 · 0 0 · 0 0 · 22 refused as planned
  • TRT-LLM on b200 runs all four checkpoints (V3.2, V3.2-NVFP4, GLM-5.2, GLM-5.2-FP8). GLM-5.2 reaches a 1,048,575-token prefix and decode KV; before this PR, TRT-LLM stopped at a prefix of 128. On h200, V3.2 and GLM-5.2 run with the BF16 KV cache.
  • vLLM runs V3.2-NVFP4 and GLM-5.2-FP8 context up to a 1,048,575-token prefix. Each GLM point yields two rows, the full layer and the skip-indexer layer.
  • SGLang runs V3.2 (FP8 block) and GLM-5.2 (BF16, 4 heads) context up to a 524,288-token prefix, and decode at batch 1–1,024 with KV up to 131,072. On h200 it measures the 90 points within its 8,192-token chunk and refuses the 22 beyond it (section 30).
  • At a8eaf74:
    • MSA GPU-free parity against 8de87d6: every difference is classified as in section 29, and the precision combos are unchanged per backend and SM.
    • MSA GPU diag, one GPU per run, pipelines 78362493–78362542:
Rows measured TRT-LLM b200 · h200 vLLM b200 · h200 SGLang b200 · h200
context 266 · 112 6 · 3 133 · 109
decode 20 · 20 4 · 2 190 · 190
refused as planned 0 · 0 0 · 0 3 · 27
  • Prefixes and decode KV reach 1,048,575. On h200, TRT-LLM's Triton reference stops at the 32K prefix its skips.py entry sets.
  • Every refusal is recorded as kernel_limit:
    • b200: 3 decode points the KV pool cannot hold;
    • h200: 24 prefills above the 8,192-token chunk and 3 decode points beyond the KV pool.
  • No other failures.
  • At 93d9976: the affected collector tests, including the rank-prefixed cases, pass (747 passed, 5 skipped).
  • At 49351ef:
    • pytest tests/unit: 14,165 passed. The one failure, test_fpm_learned, needs scikit-learn.
    • parity: 389 passed.
    • ruff, check_collector_data.py and backend_facts.py --check: clean.
    • GPU-free parity for DeepSeek-V4 and the wide-EP ops: every difference is classified as in sections 31–34, and each backend keeps its precision combinations on every SM.
    • DeepSeek-V4 GPU diag on b200 and h200, all three backends, one GPU per run, pipelines 78378929–78378944:
Rows measured TRT-LLM b200 · h200 vLLM b200 · h200 SGLang b200 · h200
context (CSA + HCA) 266 · 266 10 · 10 266 · 218
decode (CSA + HCA) 50 · 50 8 · 8 50 · 50
paged MQA logits – 4 · 4 401 · 401
top-k calibration – – 316 · 316
refused as planned 0 · 0 0 · 0 0 · 48
  • Every selected point is measured. Prefixes and decode KV reach 1,048,575 on all three backends, and the TRT-LLM series runner works on both SMs.
  • SGLang on h200 refuses the 24 CSA and 24 HCA prefills above its 8,192-token chunk, recorded as kernel_limit. There are no other failures.
  • At dbd831a:
    • pytest tests/unit: 14,097 passed. The one failure, test_fpm_learned, needs scikit-learn.
    • parity: 389 passed.
    • ruff, check_collector_data.py (R1–R7), backend_facts.py --check and the documentation-link check: clean.
  • At 10842d5:
    • pytest tests/unit: 14,139 passed. The one failure, test_fpm_learned, needs scikit-learn.
    • parity: 389 passed.
    • ruff, check_collector_data.py, backend_facts.py --check and the documentation-link check: clean.
    • GPU-free dumps of every registered op, backend and SM: the only count changes are those in sections 38–42.
    • GLM-5 sparse GPU diag on h200 (pipeline 78388967, GLM-5): every selected point is measured, with no failures. That is 130 MQA-logits rows, 260 top-k rows (two score modes) and 145 sparse-attention rows: batch 1 up to a 131,072-token prefix, plus batch 1,024 decode. On b200 (78388968, GLM-5.2-NVFP4) it measures 141, 282 and 156 rows, up to a 1,048,575-token prefix, also with no failures.
  • At 4c26239, the Repository Policy steps run locally:
    • copyright, packaged legal files and documentation links: clean;
    • CODEOWNERS: generation leaves both artifacts unchanged;
    • workflow contracts: 386 passed. The two pip-licenses tests need the CI environment.
  • Repository-root tests/ (the Full CI contracts shard, without the workflow contracts) and tests/cross_package, at 4c26239 and at main (e8cbe2d) in the same environment: identical results.
    • Root tests: 4,575 passed on both.
    • Cross-package tests: 272 passed on both.
    • Both sides fail the same tests, all for environment reasons: scikit-learn, the locally built native module, installed package metadata, legacy namespaces, and the prediction baseline commit that CI fetches first.
  • SM120 probe for commit 44: pipeline 78388970, rtx pro 6000, TRT-LLM 1.3.0rc29, the DSA modules without the raise. All 7 tasks (109 points) failed at layer construction with the fp8_ds_mla requirement quoted in section 44.
  • At b5d9582, GPU-free dumps of every op plan for the three backends at SM80–120: the only count changes are the six TRT-LLM SM120 entries in section 44.
  • At f3a9a27 (now commit 47 without commit 45):
    • pytest tests/unit/collector tests/unit/tools: 5,027 passed.
    • pytest tests/unit tests/golden and parity ran at 5516da4f1, which differs from f3a9a27 only in comment text. Unit and golden: 14,151 passed; the one failure, test_fpm_learned, needs scikit-learn. Parity: 389 passed.
    • ruff, check_collector_data.py, backend_facts.py --check, and the documentation-link, copyright and CODEOWNERS checks: clean.
  • Pro calibration smoke for commit 45: pipelines 78444833 (h200) and 78444839 (b200), SGLang 0.5.21, 168 points and 336 rows on each GPU, no failures (section 45).
  • DCO rebuild: the head's tree equals f3a9a27's plus commit 45. Commits 15–44 keep their trees, and commits 1–14 and 46–47 their patch IDs; every commit carries one Signed-off-by matching its author, and the messages are otherwise unchanged.
  • At 4d8ecf2: pytest tests/unit/collector tests/unit/tools: 5,027 passed; ruff: clean.
  • Fast CI: pending at 4d8ecf2. Full CI: pending.
  • CodeRabbit reviewed commit: pending. Codex reviewed commit: pending.

Modeling or data provenance

  • Commit 21 changes the mla_dtype label of 51,187 rows in 26 shipped TRT-LLM MLA context tables (section 21). Latencies and every other column are unchanged; no other table rows change.
  • Commit 17 changes SDK predictions for MiMo-V2-Flash and Llama 4. The one golden record that moves is refreshed; its diff is in section 17.
  • Commit 24 changes SDK predictions for Step-3.7-Flash: its sliding layers now use 96 heads, and its context length is 262,144. No golden record covers it.
  • Commit 36 replaces the per-module hash closures with one hash over the collector source. A table collected after it records the same collector_hash as every other table collected at that revision.
  • Evidence for removed filters comes from the checked-in parquet tables for vLLM 0.30.0/0.31.0, SGLang 0.5.21 and TRT-LLM 1.3.0rc29.

Follow-ups (not in this PR)

  • DeepSeek-V4-Pro top-k calibration tables. Commit 45 plans them by default; the collection itself runs separately (section 45).
  • XPU getters. The vLLM XPU gemm, attention and moe getters keep their own case lists. Moving them onto op plans needs a device key other than the SM. No XPU runner is available here, and the shipped B60 tables were collected with these lists, so the move should keep every case and be smoke-tested on a B60 by its maintainers.
  • Kernel dispatch from serving probes. The harness probes which backend serving runs, and the collector profiles that backend.
  • vLLM fail-fast cases seen in validation.
    • w4a8_mxfp4_mxfp8 for gpt-oss and Kimi-K3. Probe-based dispatch decides which MXFP4 path vLLM serves them with.
    • The NVFP4 Qwen3.8 alias, which has no packaged config.
  • TRT-LLM sparse MLA on SM120. Time the DSA and DeepSeek-V4 modules on the packed fp8_ds_mla KV pool that TRT-LLM requires there (section 44).
  • Dated comments. 62 comment lines in 25 collector files still date a source audit or a hardware observation (section 47).
  • w4a16_mxfp4 on SM80/86. collector/skips.py keeps it off these SMs on every backend, carried over from the old floor. There is no recorded reason for vLLM, so recheck it against a vLLM run.

Tracking

🤖 Generated with Claude Code

@copy-pr-bot

copy-pr-bot Bot commented Oct 31, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Oct 31, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

@tianhaox tianhaox changed the title feat: plan GEMM cases once for every backend feat: add collector notes and plan GEMM cases once for every backend Oct 31, 2026
@tianhaox tianhaox changed the title feat: add collector notes and plan GEMM cases once for every backend feat: add collector notes and plan GEMM and MoE cases once for every backend Oct 31, 2026
@tianhaox tianhaox changed the title feat: add collector notes and plan GEMM and MoE cases once for every backend feat: add collector notes and one case plan per op for every backend Oct 31, 2026
@tianhaox tianhaox changed the title feat: add collector notes and one case plan per op for every backend feat: plan collector ops once for every backend Nov 1, 2026
tianhaox and others added 23 commits November 2, 2026 08:56
The change-to-evidence chain (evidence_policy.yaml, evidence_check.py and
the evidence waivers) was never wired into CI: changed_ops.py produced a
manifest nobody read, the resolver ran only in unit tests, and no code read
the exceptions registry. Remove it and give known deviations one
machine-checked home.

- Delete evidence_policy.yaml, evidence_check.py, its tests and
  evidence_exceptions.yaml.
- Move the three standards exceptions to collector/notes/<backend>.yaml as
  deviation notes (D-vllm-compute-scale-standalone,
  D-vllm-fp8-static-derived, D-trtllm-mhc-unfused). Code and data comments
  cite the note id instead of restating it.
- Add check_collector_data.py rule R8: notes match a closed schema, and no
  note covering the pinned version has a verified_on behind the pin of a
  table in its scope.
- Add .claude/rules/collector/overview.md (Pin, Plan, Record, Note, Rule;
  the failure principle; collection cost; what to record when) and align
  the rule files, the collector skills, AGENTS.md, the CodeRabbit collector
  instructions and the collector docs with it. Kernel-limit guards are now
  reserved for cases that would mismeasure, hang or waste significant time.
- Keep changed_ops.py as an informational PR report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
The three GEMM getters each carried their own SM ladder, shape filters and
case order. They now call one typed plan, case_generator.plan_gemm(backend,
sm): the gemm_types axis of gemm.yaml times the shared shapes, minus whole
dtype regions that capabilities.yaml rules out.

- capabilities.yaml gains backend_gaps: a whole (op, dtype) region on listed
  SMs that one backend's collector cannot run, citing a guard note. The one
  entry is SGLang fp8_block on SM89 and SM120
  (G-sglang-gemm-fp8-block-deepgemm-only): the collector calls DeepGEMM
  directly, while serving runs Triton on SM89 and CUTLASS on SM120.
- Removed per-framework filters: the SGLang and TRT-LLM skip of block-scaled
  n<128 or k<128 (vLLM 0.30.0 on b200 has rows for all 6,808 such cases with
  n and k multiples of 16), TRT-LLM's m*n or m*k >= 2^31 and fp8_block
  n*k >= 2^31 skips (SGLang and vLLM have rows for them),
  AIC_COLLECT_GEMM_TYPES, and SGLang's seed-42 shuffle.
- Removed the vLLM fp8_block n%16/k%16 raise; its natural failure is a cheap
  exception. ai-dynamo#649 removed the matching TRT-LLM fp8 raise, and the TRT-LLM
  op-cache test now checks that an unaligned fp8 case runs.
- GemmCase is a NamedTuple whose str() keeps the list form, so case IDs,
  checkpoints and --case-filter strings do not change. vLLM's GEMM case set
  and case_plan_hash are identical at every SM, and TRT-LLM keeps its order.
- With --sm or --gpu, --plan-only reports typed plans: cases per consumer
  key, the regions left out and plan_hash. --plan-table writes one CSV row
  per case.
- SGLang's FIXME(re-verify) serving gaps become deviation notes
  D-sglang-gemm-bf16-cutedsl, D-sglang-gemm-nvfp4-act-quant and
  D-sglang-gemm-fp8-block-triton-fallback. The last one also covers the
  SGLang fp8_block cases with n=32 or k<128 that the plan now queues on
  SM90/100: serving runs Triton there, the collector times DeepGEMM. The
  stale vLLM SM120 fp8_block comment is gone: the 0.30.0 RTX PRO 6000 table
  has 31,598 fp8_block rows.
- The rules, READMEs, docs and both collector skills describe typed plans
  and backend gaps. The GEMM hash closures now include capabilities.yaml.

Per-system plan growth (SGLang / TRT-LLM): SM89 0 / +8,269, SM90 +7,918 /
+8,269, SM100 and SM103 +15,836 / +16,228, SM120 +7,918 / +16,228. vLLM is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
The GEMM plan was GEMM-specific code: plan_gemm, a TypedPlan wrapper and a
GEMM-only test file. Moving the next op would have meant the same again. One
generic plan now serves every op, and moving an op is a declaration.

- collector/op_plans.py holds one OpPlan entry per op: its case type, an
  expand function (every case, in run order), its dtype field and the fields
  that share one module build. plan(op, backend, sm) removes only the whole
  regions that a capabilities.yaml floor or backend gap rules out. Each
  getter is one line, plan_cases("<op>", "<backend>", get_sm_version()).
- An op may declare rows for a backend whose run function takes more than
  the shared case. Rows say how a case runs, never whether: a case with no
  row raises.
- backend_gaps entries name a region by dtype or by another region field the
  op plan declares. The loader rejects any other field and any op without a
  plan.
- tests/unit/collector/test_op_plans.py runs over every OP_PLANS entry:
  one-line getters, backends differing only by backend gaps, whole-region
  removal, and the list form of case strings. It replaces test_gemm_plan.py.
- GEMM queues exactly the same case list as before, in the same order, for
  SGLang, TRT-LLM and vLLM at SM80, 86, 89, 90, 100, 103 and 120. The plan
  report counts cases per dtype.
- op_plans.py joins the GEMM hash closures; the rules, READMEs and skills
  describe op plans.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
The three MoE getters each read their own quant-mode list, model policy
and parallel layouts, and planned 7,503 (SGLang), 9,552 (TRT-LLM) and
3,135 (vLLM) layer cases on SM100 from the same model rows. MoE now moves
onto the op plan with no change to the plan mechanism: a MoeCase type, an
expand function and two rows adapters in op_plans.py, and one-line
getters.

- One quant-mode axis, common_case_values.moe.quantization_modes, replaces
  the moe_sglang, moe_trtllm and moe_vllm lists. A model row declares its
  checkpoint's modes in quantization_modes, the same for every backend; a
  row that declares none plans every mode. allowed_model_paths keeps
  int4_wo and the MXFP4 formats to the models that ship them. MoE rows may
  no longer declare frameworks or per-backend allowed_modes;
  framework_quantization keeps a backend's module_config and vllm_xpu's
  policy only.
- The shared expand dedupes rows that time the same layer and raises when
  two rows would time different layers under one consumer key, once for
  all backends instead of three getter copies.
- Rows say how a case runs: SGLang's adds the row's routing, activation
  and the runner pinned for the SM; TRT-LLM's adds min_latency_mode and,
  for nvfp4 on SM100/103 with at most 256 experts, the min-latency row.
  For every case the old getters queued, the rows are unchanged.
- capabilities.yaml backend gaps, each citing a guard note verified at the
  pin: vLLM tp>1 with ep>1 (MoE region parallel=tp_ep, flattened into EP
  by FusedMoEParallelConfig.make), vLLM w4a8_mxfp4_mxfp8 on SM120, SGLang
  per-tensor fp8 (no collector path), SGLang int4_wo on SM80/86 and
  w4a8_mxfp4_mxfp8 on SM120 (no verified runner pin), and TRT-LLM int4_wo
  on SM100/103 (no MoE implementation at 1.3.0rc29; the worker raise it
  replaces is gone).
- Per-backend SM gates without a current-pin citation are deleted, and
  those cases now run and record failures: SGLang and TRT-LLM fp8_block on
  SM89, TRT-LLM int4_wo below SM100 and w4a16_mxfp4 on SM89/120.
- Plan change on SM100 (cases): SGLang 7,503 -> 8,274, TRT-LLM 9,552 ->
  9,864 (11,697 rows with min-latency), vLLM 3,135 -> 3,498. Removed:
  bf16/fp8_block on the vLLM-only Nemotron latent width for SGLang and
  TRT-LLM, per-tensor fp8 on the GLM-5 shape (no such checkpoint), TRT-LLM
  int4_wo on SM100/103, and vLLM w4a16_mxfp4 below the SM89 floor.
- The TRT-LLM seed-42 shuffle is gone; cases keep the shared order.
- WideEP callers of get_common_moe_test_cases drop the backend argument.
  The vLLM EP population keeps its 46 distinct invocations; the duplicate
  fp8_block invocations under the GLM-5 BF16/NVFP4 names go.
- MoE tests now exercise the shared plan; op_plans.py, capabilities.yaml
  join the MoE hash closures; the rules, READMEs and skills describe it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
Both ops already queued the same cases on every backend, through three
copies of the same getter. They move onto op plans; each queues exactly the
case list it queued before, in the same order, for SGLang, TRT-LLM and vLLM
at SM80, 86, 89, 90, 100, 103 and 120.

- compute_scale: ComputeScaleCase (m, k), dtype fp8. The getters' SM <= 86
  check was the compute_scale op floor in capabilities.yaml, which the plan
  applies. The shared sweep moves from framework_specific_op_cases.trtllm to
  all_frameworks_op_cases in compute_scale.yaml; the op is still opted in
  per backend by the Qwen3 static-FP8 artifact only.
- encoder_attention: EncoderAttentionCase (batch, seq, heads, head_dim),
  bf16. The token budget (128K) and the 32-bit activation-index budget the
  three getters each applied now live once in the expand, which logs how
  many cases they leave out.
- case_generator.get_shared_base_op_case_specs reads the specs an op plan
  shares and rejects a CUDA backend's own case list for that op.
- The docs point at OP_PLANS instead of naming the migrated ops; the hash
  closures of both ops include op_plans.py and capabilities.yaml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
Which kernel or runner a backend measures is what Rule 2 checks against
serving, but SGLang's MoE runner was chosen inside its rows, where rows are
meant to add execution arguments only.

- OpPlan gains dispatch: backend -> the kernel or runner the plan picks for
  a case. Rows receive it; --plan-only reports by_dispatch and
  --plan-table adds a dispatch column, so a checker can match each case
  (model, dtype) against what serving was observed to run. SGLang MoE's
  runner pin moves from its rows to dispatch; its queued rows are
  unchanged.
- Plan logs go to the collector log. The MoE and encoder-attention expands
  printed to stdout and broke the --plan-only JSON.
- test_op_plans.py checks that planning imports no framework (SGLang, vLLM,
  TensorRT-LLM and torch blocked), that region fields stay a few string
  categories, and that --plan-only reports and tables every op plan.
- case_authoring.md and the op-development skill describe dispatch: a field
  that enters the persisted key or changes the kernel belongs in the case
  or the dispatch, not in rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
GDN (SGLang, TRT-LLM, vLLM), KDA (SGLang, vLLM) and Mamba2 (TRT-LLM) move
onto op plans. Each queues exactly the case list it queued before, in the
same order, at SM80, 86, 89, 90, 100, 103 and 120.

- GdnCase carries the model's mamba_ssm_dtype, which SGLang's run function
  takes; TRT-LLM's and vLLM's rows drop it, as their run functions take no
  state dtype. The generator already restricts it to float32, the only
  state dtype the persisted GDN key can represent.
- KdaCase and Mamba2Case are the getters' list forms. Generation cases keep
  no sequence sweep, as before.
- Every case type now prints through one list-form helper.
- The --plan-only test requests the op plans each backend registers; the
  Qwen3.5 GDN test checks the plan of all three backends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
mla_bmm_gen_pre and mla_bmm_gen_post move onto op plans. Each backend
queues exactly the case list it queued before, in the same order, at SM80,
86, 89, 90, 100, 103 and 120.

- The getters' dtype filters become declarations. The SGLang and TRT-LLM
  SM gate on fp8 was the fp8 floor in capabilities.yaml. vLLM's bf16-only
  set becomes a backend gap on fp8 for both ops, citing the new guard note
  G-vllm-mla-bmm-fp8-not-served: on NVIDIA vLLM runs both BMMs as bf16
  torch.bmm and its fp8/fp4 BMMs are ROCm AITER only (checked at v0.30.0).
- get_mla_bmm_case_specs without a backend reads the shared sweep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
mla_context and mla_generation (SGLang, TRT-LLM) move onto op plans. Both
collectors built the same MLA catalog per KV dtype and TP, deduplicated on
the loader key (dtype, local heads, batch, input length) at the smallest
TP; the shared expand does that once.

- Rows build the run tuples the collectors queued before: SGLang's torch
  dtype and the page size of its MLA backend, TRT-LLM's DataType and KV
  block size. Both need the framework, so a Plan now builds its rows on
  demand: planning stays framework-free, and --plan-only on a CPU counts
  the cases but reports plan_hash null with the missing module.
- SGLang's MLA backend map covers SM89/90/100/103/120; SM80/86 become a
  backend gap (G-sglang-mla-backend-unmapped) where the getter raised.
- SGLang no longer drops fp8 KV on its triton platforms (SM89/120); that
  filter had no citation at the pin, so those cases run and record
  failures. TRT-LLM loses its fp8 cases on SM80/86, below the fp8 floor.
- The SGLang backend selector drops its 0.5.14 version narration; the
  mapping is the current pin's, and git keeps the history.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
mhc_module (SGLang, TRT-LLM, vLLM) moves onto an op plan. A case is one
mHC call site of an architecture at one (hidden_size, hc_mult), with its
token sweep; models that share a geometry reuse it.

- Rows keep each backend's run unit and IDs: TRT-LLM and vLLM queue one
  row per token count, SGLang one row per site that sweeps every token
  count in one worker. TRT-LLM's 140 rows, vLLM's 175 GLM-5.3-Flash rows
  and SGLang's 4 DeepSeek-V4 rows are unchanged at every SM.
- The runtime architecture gates become backend gaps on the new region
  field architecture, each citing a guard note:
  - SGLang, GLM-5.3-Flash (G-sglang-mhc-glm5next-not-ported): its 4 cases
    always failed the runtime audit.
  - vLLM, DeepSeek-V4 (G-vllm-mhc-dsv4-shared-key): its 140 cases always
    failed the runtime audit, which keeps DeepSeek-V4 rows out of the
    table GLM-5.3-Flash rows share a key with.
  - TRT-LLM, GLM-5.3-Flash (G-trtllm-mhc-glm5next-not-served): no row
    change; the getter already folded GLM into DeepSeek-V4's rows.
- The getters' --smoke shortcut to 16 tokens is gone; --smoke samples
  four rows like every other op.
- --plan-only's plan_hash takes an {id, params} row's own id, the ID a
  run records for it; one helper now builds both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
The MoE validation runs at b012f30 (plan v4moe_20261031b) showed three
regions where TensorRT-LLM's MoE resolution turns down every candidate at
layer build, whatever the shape. They become backend gaps:

- int4_wo on every SM (G-trtllm-moe-int4-wo-no-implementation, extended
  from SM100/103): CutlassFusedMoE does not support W4A16. Observed on
  a100, l40s, h200 and b200; no TensorRT-LLM table holds an int4_wo row.
- fp8_block on SM89 and w4a16_mxfp4 on SM89/SM120
  (G-trtllm-moe-cutlass-sm-support): below SM100 CutlassFusedMoE is the
  only candidate, and it runs FP8_BLOCK_SCALES on SM90/SM120 and
  W4A16_MXFP4 on SM90 only. Every such case failed on l40s. The
  rtxpro6000 run is still queued; the SM120 entry follows the support set
  the resolution reports, and no SM120 table holds a w4a16_mxfp4 row.

TRT-LLM MoE cases queued: SM80/86 2,163 -> 2,049; SM89 6,459 -> 3,753;
SM90 6,459 -> 6,345; SM120 9,978 -> 9,537; SM100/103 unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
The op-plan rules now say what commits 9 and 10 added: a row may be an
{id, params} mapping that a run records (and --plan-only hashes) under
its id, and rows that hold framework objects are built only when the
getter runs, so --plan-only on a CPU reports plan_hash null for them.
The layer permissions name mHC's architecture region field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
The rtx pro 6000 validation run (plan v4moe_20261031b, pipeline 78153191)
rejected every w4a16_mxfp4 and int4_wo MoE case at layer build, so the
SM120 entries of G-trtllm-moe-cutlass-sm-support and
G-trtllm-moe-int4-wo-no-implementation are now observed rather than
inferred. The cutlass note also cites why serving has no other
implementation on SM89/SM120: TritonFusedMoE runs on SM90 only, the
TRTLLM-Gen implementations on the SM100 family only, and every other
backend falls back to CUTLASS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
Llama 3.1 405B has 128 query and 8 KV heads. At TP1, as under pipeline
parallelism, one rank runs all of them. No base grid has that shape: the
context grids stop at 96 query heads (64 on vLLM), and vLLM's decode grid
at 64. The Llama profile now declares the 405B attention row for TP 1, 2,
4 and 8. The smaller TPs deduplicate into the base grids.

The row applies to every backend. On attention rows, frameworks can only
exclude SGLang.

Cases added per SM (SM80/86 in parentheses):
- TRT-LLM: +357 (+119) context cases; generation unchanged.
- vLLM: +238 (+119) context and +256 (+128) generation cases.
- SGLang, SM89-120: +357 context cases; generation unchanged.

SGLang cases name the architecture of the profile that owns the shape.
1,071 context and 1,168 generation cases of the 405B shapes now name
LlamaForCausalLM instead of Qwen3MoeForCausalLM or none. Only their case
IDs change: neither architecture triggers SGLang's FlashInfer workspace
override, and the perf row does not record the architecture.

The SGLang SM90 structural population test now expects 162 context
shapes (was 161); generation stays at 147.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
collector/skips.py is now the one place that keeps cases out of a
collection. Each entry names what it skips (backend, op, sm, model, dtype
or any case field), and the comment above it says why. There are no ids,
no notes and no framework versions. An entry belongs there only when the
cases would be mismeasured, would hang, would fail as a whole block, or
come from a model the backend cannot serve.

Moved into SKIPS and deleted:
- cases/capabilities.yaml: the dtype_min_sm and op_min_sm floors, now
  explicit SM lists, and the 16 backend_gaps;
- the 12 G- guard notes in collector/notes/*.yaml, condensed into the
  entries' comments. The D- deviation notes stay, and R8 now accepts only
  deviations;
- cases/denylist.yaml (empty) and its loader;
- collector/capabilities.py: detect_sm_version and case_dtypes move to
  skips.py.

Op plans apply SKIPS when they expand; collect.py and fullnode.py apply it
around every other getter and before queueing. A model entry drops a case
file's rows before cases are merged, so a shape another model also needs
is still collected, and a model run (--model-path) plans no op its backend
skips. --plan-only reports what each entry left out, with the entry itself
as the reason.

New: four TRT-LLM model entries, for MiMo, MiMo-V2-Flash, Muse Glimmer and
GLM-5.3-Flash, for which TRT-LLM registers no model class. The
GLM-5.3-Flash entry replaces the TRT-LLM mHC architecture gap.

GPU-free parity against 88c0f92 (every op plan and both attention ops,
3 backends x SM 80/86/89/90/100/103/120, getter rows and the rows left
after the queue filter): identical except the TRT-LLM rows of those
models, removed in order with nothing added. Per SM89-120:
- attention context -6,324 and generation -6,042 (SM80/86: -2,108 and
  -3,021): MiMo-V2-Flash head_dim 192 (9,604) and Muse Glimmer window
  2048 (2,762);
- GEMM -2,960 (-2,220 on SM89/90, -740 on SM80/86): GLM-5.3-Flash's
  off-grid widths at k=4096;
- MoE rows -783 on SM100/103 (-666 SM120, -450 SM90, -234 SM89, -117
  SM80/86): MiMo-V2-Flash and GLM-5.3-Flash.
Model runs: TRT-LLM plans no op for the four models, and vLLM's
DeepSeek-V4 run no longer schedules mhc_module, whose rows were already
all skipped.

collector_hash: SHARED_CORE names skips.py instead of capabilities.py, so
every module's hash changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
…armed

Follow-up to a ai-dynamo#649 review comment. If the worker dies between the fork
and the child's prctl(PR_SET_PDEATHSIG), the child has already been
reparented and never gets the signal. The preexec_fn now captures the
worker pid in the parent and, after prctl, exits the child when its
parent is no longer the worker.

The docstring also notes that PDEATHSIG follows the forking thread,
which is the worker's main thread: run_measurement_subprocess is only
called from there.

Two unit tests cover the armed case and the orphaned case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
…erve them

MiMo-V2-Flash's hybrid_layer_pattern marks sliding-window layers with 1.
vLLM builds its SWA attention where the pattern is 1, and SGLang lists
those layers as its SWA layers. The SDK read 1 as global, so it modeled
39 global and 9 SWA layers. It now models 9 global layers (one of them
with a dense FFN) and 39 SWA layers.

Llama 4 attends globally only in its layers without RoPE: where
no_rope_layers is 0 or, when that list is empty, every
no_rope_layer_interval-th layer (default 4), as transformers fills it.
The other layers use chunked local attention. The SDK alternated even
and odd layers, giving 24/24; it now gives 12 global and 36 chunked
layers.

Golden diff: only mimo-v2-flash-b200-vllm-hybrid-attn-xshape moves,
refreshed with pin_goldens.py --refresh:
- static: ctx 22.82 -> 21.44 (-6.1%), gen 4.392 -> 4.437 (+1.0%),
  total -4.9%;
- agg: TPOT -5.6%, TTFT -2.1%; disagg: TTFT -6.1%, TPOT +0.2%.
The Llama-4 Scout case is unchanged: at isl 1024 its 8192-token chunk
covers the whole sequence, so both layer types cost the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
Attention moves onto the op plans with one case list for SGLang, TRT-LLM
and vLLM. The shared grid is the generic full attention at head_dim 128
that all three backends already collected, plus every model's attention
profile on every backend. The per-backend base grids are gone: their
other shapes (head_dim 64 and 256 grids, window 128, head counts only
one or two backends swept) were an exact key for no packaged model, and
a model that needs one declares a profile.

- A profile carries the model's attention properties: v_head_dim,
  has_attention_sink, and SGLang's runtime window, chunk size, scaling
  and backend per SM. SGLang's separate variant rows (gpt-oss, Muse
  Glimmer, Nemotron-H) fold into the shared profiles, `frameworks:` is
  retired, and each backend's rows take what its run function supports.
- SGLang's attention backend is the plan's dispatch. A shape that two
  models serve on different backends (Nemotron-H on flashinfer, the
  other models and the generic grid on trtllm_mha) runs on both; on one
  backend a model's semantics win over the base grid's, and two models
  that disagree raise.
- A collector/skips.py entry may name a `dispatch`: it leaves the case
  out on that kernel only. The SGLang refusals that used to fail at run
  time become such entries: a BF16-compute prefill on an FP8 KV cache on
  fa3 (head_dim <= 256) or trtllm_mha, and an FP8-compute prefill on
  flashinfer (8.7k to 13.6k error records per SM89-120 system). Also
  entries: vLLM's missing FP8-compute input, SGLang on SM80/86, and the
  two SM90 TRT-LLM FP8-KV decode crashes, which no shared shape reaches.
- TRT-LLM serves Gemma 4 on its default TRTLLM backend
  (Gemma4ForCausalLM.get_model_defaults), so its rows run there. The
  FlashInfer routing and code path go, with the guard that refused every
  Gemma 4 case on SM89/90/120 and the FlashInfer FP8 guard. So do the
  head_dim 192 guard (TRT-LLM cannot serve MiMo-V2-Flash), the dead
  SM120 FP8-FMHA helper and the import shims for older releases.
- One decode budget for every backend: T * 128 // min(head_dim, 128) //
  len // heads, the largest of the three formulas. The TRT-LLM XQA
  head-ratio skip on SM100+ is deleted.
- Deviations are commented next to the rows: vLLM times MiMo-V2-Flash
  with V = 192 (serving's V head is 128) and gpt-oss without sinks;
  TRT-LLM adds sinks to every head_dim 64 row, Kimi-K3's draft attention
  included. The stale "vLLM omits it" comments on op activation now say
  what model runs and full runs do.

Cases queued per SM, GPU-free against 5aeba48 (getter plus queue
filter, SM80-120; SM89/103 match SM90/100 except where noted):

| | SM90 before -> after | SM100 before -> after |
|---|---|---|
| SGLang context | 56,256 -> 29,784 | 64,008 -> 35,326 |
| SGLang generation | 45,072 -> 36,096 | 51,550 -> 41,370 |
| TRT-LLM context | 77,727 -> 36,261 | 73,443 -> 36,261 |
| TRT-LLM generation | 53,834 -> 30,054 | 52,810 -> 30,054 |
| vLLM context | 54,706 -> 28,390 | 54,706 -> 28,390 |
| vLLM generation | 67,892 -> 36,096 | 67,892 -> 36,096 |

Every difference is one of:
- removed: shapes outside the shared grid (13.7k SGLang, 41.5k TRT-LLM,
  26.3k vLLM context rows on SM90); the SGLang refusals above, which
  were queued and failed before; SGLang trtllm_mha rows of head_dim 256
  shapes only the old SGLang grid planned on that backend (SM100/103);
- added: decode points the shared budget admits for head_dim != 128
  (SGLang +840, TRT-LLM +298, vLLM +840 on SM90); TRT-LLM GQA ratio 40
  and 48 context rows on SM100+ (+714);
- changed: TRT-LLM Gemma 4 rows' backend element FLASHINFER -> TRTLLM;
  SGLang rows whose shape several models plan now name the first model
  in file order (Muse Glimmer or Nemotron-H instead of Qwen3), which
  changes the case ID only.
SGLang on SM80/86 queued nothing before (its getter raised) and after.
Rows keep each backend's run arguments, so every other case keeps its
ID. The vLLM XPU collector keeps its own grid; its 145 head keys per
phase are unchanged except that MiMo-V2-Flash's sliding-window keys now
carry their sinks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
A collector/skips.py entry may name the dispatch kernel when a backend
runs one case on two kernels and only one of them cannot run it (the
SGLang attention entries in 4622201). The layer permission table and
the case-authoring lookup table now say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
The DSA, MSA and DeepSeek-V4 attention-module collectors gain a series
entry: one task measures several (seq_len, prefix_len) points of one key
(model, heads or TP, precision, batch). The op-plan batch rows will call
it. Today's getters, registry entries and case IDs are unchanged, and
each single-point worker runs a series of one.

What a series shares and what it does not:
- The layer comes once from the per-worker layer cache, as before.
- Each point still builds its own KV cache manager, metadata and inputs
  and tears them down, exactly like a one-point case. The manager's
  capacity is a kernel input: the attention metadata reads max_seq_len
  from the manager, DeepSeek-V4 sizes its compressed-index buffers from
  it, and the MSA indexer copies the whole index-K pool every forward.
  One manager sized for the largest point would change what the smaller
  points measure, so no perf row field or value changes.
- The checking dry run runs once per seq_len.
- Points run largest first.

A point that fails for a non-fatal reason is recorded, and the series
continues; a sticky CUDA error aborts it. Failures are raised after the
series: a single failure as it is, several as a summary that keeps the
kernel-limit classification when every failure is a kernel limit.

run_mla_module now releases the KV pool in a finally block, as the MSA
and DeepSeek-V4 collectors already did, so a failed point does not leak
its pool into the next one. collector/trtllm/module_series.py joins the
hash closures of the three collectors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
TRT-LLM's MLA context attention follows the KV cache dtype: with an FP8
KV cache it quantizes Q/K/V and runs FP8 FMHA on SM90/100/103/120
(thop/attentionOp.cpp), whatever the BF16 inputs say. The MLA module
collector labelled every row mla_dtype=bfloat16, so its FP8-KV context
rows claimed BF16 compute. The SDK looks these rows up with fmha=fp8 for
FP8 checkpoints (DeepSeek-V3/R1, Kimi-K2), so on TRT-LLM those lookups
missed and fell back to op-level ops. The op-level collector got the
same fix in ai-dynamo#264.

- collect_mla_module.py labels MLA context rows with an FP8 KV cache on
  SM90/100/103/120 as mla_dtype=fp8, like collect_mla.py.
- The shipped tables get the same label: 51,187 rows in 26 files, the
  module context tables at rc20 and rc29 on b200, b300, gb200, gb300,
  h100, h200 and rtx_pro_6000, the op-level context tables at rc29, and
  the op-level rc20 tables ai-dynamo#264 did not touch. Latencies and every other
  column are unchanged.
- op_backend_facts.yaml follows (49 entries); backend_facts.py --check
  and check_collector_data.py pass.
- Tests follow:
  - test_power_data pins the new hash of b200's rc20 module table;
  - the large-EP task test now expects fp8 FMHA on the FP8 KV cache, with no fallback;
  - the wide-EP compile-engine parity case now uses fp8 FMHA on the FP8 KV cache, which
    is what TRT-LLM runs. Its golden is unchanged, because it reads the same rows, relabelled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
…s on Blackwell

The TRT-LLM DeepSeek-V4 module collector timed a BF16 KV pool on
SM100/103. The probe behind that choice tried only the packed
fp8_ds_mla pool, which is not servable off Hopper (it needs 256-token
blocks and the fallback attention refuses it). Blackwell serves the
plain FP8 pool: it is TRT-LLM's recommended DeepSeek-V4 configuration
(examples/models/core/deepseek_v4/README.md) and what its B200 CI runs.
The SDK already looks DeepSeek-V4 up with an FP8 KV cache, so the BF16
rows were never read by default.

The collector now times FP8 KV on every SM. Hopper keeps the fp8_ds_mla
pool it requires. AIS_DSV4_KV_CACHE_DTYPE still overrides it. The SDK
comment that claimed FP8 KV is required everywhere now says what each
backend serves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
TRT-LLM has no separate precision for MLA attention. On
SM90/100/103/107/120, an FP8 KV cache turns on FP8 context MLA
(mFP8ContextMLA in thop/attentionOp.cpp), and generation MLA runs FP8
for an FP8 KV cache on every SM. BF16 compute on an FP8 KV cache is
therefore not a configuration TRT-LLM can run there. Since commit
7d925f6 the shipped tables say so, and an explicit fmha=bfloat16 on
an FP8 KV cache found no rows.

- helpers.trtllm_mla_runs_fp8 states the rule for the MLA families
  (DeepSeek-V3/R1, Kimi-K2/K2.5/K3).
- helpers.resolve_trtllm_mla_compute resolves an explicit bfloat16 to
  fp8 there and logs a warning. It sits next to the SGLang MLA resolver,
  on the same call sites: the estimate path, KV-cache memory, the
  inference session and the estimate-path FMHA guard.
- The Task resolves it before validation, so an explicit bfloat16 does
  not fail validate.
- An unset FMHA is left to the checkpoint inference, which already
  picks fp8 for an FP8 KV cache.
- A bfloat16 KV cache, SM80/89, other backends and whole-model FPM are
  untouched.
- The engine path loads the system spec only when the request is
  bfloat16, so a malformed system YAML still raises where it did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
tianhaox and others added 24 commits November 2, 2026 08:56
The packaged Step-3.7-Flash configs were normalized by hand from the
upstream text_config when the model was added (AIConfigurator #1447
and #1452). Two upstream values were dropped:
- attention_other_setting, which gives the 33 sliding-attention layers
  96 query heads (8 KV groups, head_dim 128) against the 64 heads of
  the global layers;
- the context length, written as 65,536, while every upstream revision
  says 262,144.

The SDK parser already reads attention_other_setting, so it sized
every sliding layer with 64 heads. Meanwhile the collector plans the
96-head shapes that vLLM, SGLang and TRT-LLM build.

Both files now carry the upstream values at the pinned revisions:
stepfun-ai/Step-3.7-Flash@5f62440
and -FP8@b3d7916fccac844cca050d7520f2aaa513f9a84f. Both copies of
THIRD_PARTY_NOTICES.md record the files, the revisions, the Apache-2.0
license and every normalization. check_packaged_legal_files.py passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
A module collector builds the module once and times several points on
it. Per-case rows cannot express that, so an op plan may now declare
batch_rows for a backend:
- The function receives the kept cases and returns (row, the cases it
  times) pairs.
- A row lists its points explicitly; the run never derives them again.
- Plan.rows checks that every kept case is timed by exactly one row and
  that no row times a case the plan left out.

A backend declares either batch_rows or rows/dispatch, never both.

dtype_field may also name several fields, for a case that carries its KV
cache, compute and GEMM dtypes separately. A skips.py dtype entry matches
any of them, and the plan summary counts each combination as one key,
such as bfloat16/fp8/fp8_block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
mla_context_module and mla_generation_module now expand one sweep for
TRT-LLM and vLLM: the mla_module_plan section of
cases/base_ops/mla_module.yaml.
- Heads are the union over the MLA models: DeepSeek-V3's shards (128 to
  1) and Kimi-K3's (96, 48, 24, 12), all measured on the canonical
  DeepSeek-V3 module.
- Precisions are the union of the combinations either backend
  collected. Three skips.py entries keep each backend's set as it was:
  FP8 compute stays off SM89/90, TRT-LLM has no FP8-compute context case
  of its own, and vLLM keeps SM89 to BF16 KV.
- TRT-LLM queues one run_mla_module_series_worker task per (model,
  heads, precision, batch) with its (seq_len, prefix_len) points. vLLM
  keeps its series rows.

The plan no longer depends on the device. vLLM's generation getter used
to drop points whose KV cache could not fit the device. The run now
refuses those points before allocating them, with a FIXME(kernel-limit)
"does not fit" reason, and sizes the module for the points that fit.
H100 and H200 therefore get the same plan. The DSA module getters keep
the generation-time filter.

GPU-free parity against the previous getters at SM 80, 86, 89, 90, 100,
103, 120 and 121:
- Each backend keeps its precision combinations and (batch, length)
  grid on every SM.
- TRT-LLM gains heads 96, 48, 24 and 12, which vLLM already timed, on
  each of its own precision combinations.
- vLLM's rows are unchanged with the memory filter off. On each device,
  the run refuses exactly the points the filter dropped.

--plan-only now sends planning output to stderr. The MLA module loader
prints notes about the models it collapses, and stdout must carry only
the JSON report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
dsa_context_module and dsa_generation_module now expand one plan for
TRT-LLM, vLLM and SGLang from the mla_module_plan section of
cases/base_ops/mla_module.yaml.

- Grid: a context point (batch, isl, prefix) follows serving's chunked
  prefill: batch x isl <= 16384 new tokens, batch x (isl + prefix) <= 1M
  KV tokens, and prefix + isl within the model's max_position_embeddings.
  Prefixes are 13 anchors at x2 spacing up to 1048575, keeping the 2048
  top-k knee; from a 16K prefix on only isl 1, 512, 2048, 8192 and 16384
  run. Decode keeps the shared grid and adds KV lengths 262144, 524288
  and 1048575 at batch 1, 2 and 4 where the model's context allows them.
- Models: one checkpoint per (architecture, projection GEMM), the
  longest-context one: DeepSeek-V3.2, DeepSeek-V3.2-NVFP4 (it keeps o_proj
  in NVFP4), GLM-5.2 and GLM-5.2-FP8. A GEMM no checkpoint runs is timed
  on the architecture's longest-context checkpoint. TRT-LLM moves from
  GLM-5 to GLM-5.2.
- Precisions: the plan holds the union of the combinations the backends
  collected. skips.py entries keep each backend's set: TRT-LLM has no FP8
  KV on SM90, and SGLang times only a checkpoint's own projection GEMM,
  with no NVFP4 projections. SGLang also leaves out the one-token prefill
  and NVFP4 checkpoints below SM100, as its getter did.
- Rows: TRT-LLM queues one run_mla_module_series_worker task per module,
  vLLM series list their (isl, prefix) points, and SGLang rows carry their
  [batch, isl, prefix] points into the subprocess, which no longer derives
  them. SGLang's FP8-KV attention backend is the plan's dispatch, so batch
  rows may now come with a dispatch.

The plan no longer depends on the device. SGLang refuses a point beyond
the prefill chunk it uses on the device or beyond its KV pool, and vLLM a
decode point whose KV cache cannot fit, with a FIXME(kernel-limit) reason
before allocating it. The AIC_DSA_CONTEXT_* switches are gone. The
per-backend sweep sections keep only what the MSA modules, the wideep MLA
modules and the GLM-5 sparse kernels read, and the TRT-LLM and vLLM module
CLIs run the plan's rows.

GPU-free comparison with the previous getters at SM 80 to 120 (SM80/86/89
skip these ops before and after):
- Each backend keeps its precision combinations per architecture on every
  SM.
- Points per SM90 / SM100+ (context, decode): TRT-LLM 4,880 / 14,640 ->
  11,110 / 33,330 and 3,680 / 11,040 -> 3,770 / 11,310; vLLM 71,660 /
  107,490 -> 22,220 / 33,330 and 7,360 / 11,040 -> 7,540 / 11,310; SGLang
  57,344 -> 14,590 and 3,656 -> 5,700 on every SM.
- SGLang gains the TP16 head shard its getter left out and moves to the
  shared grid: its own batches 512 and 1024, isl 4 and 8, 26-value prefix
  list and per-model ceiling points go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
dsa_context_module_skip_indexer and dsa_generation_module_skip_indexer
expand the DSA module plan for the checkpoints whose layers share one
top-k index (index_topk_freq > 1, read from the packaged config): GLM-5.2
and GLM-5.2-FP8. Only SGLang collects them; their rows are the full DSA
modules' SGLang rows of those checkpoints, and the registry still binds
skip_indexer=True. The getters are the one-line plan call.

GPU-free comparison with the previous getters, on every SM from SM90:
context 40,016 -> 9,800 points (176 -> 180 rows) and decode 2,464 ->
3,860 points (16 -> 20 rows), the same grid classes as the full modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
msa_context_module and msa_generation_module now expand one plan for
TRT-LLM, vLLM and SGLang from the mla_module_plan section of
cases/base_ops/mla_module.yaml.

- Grid: MiniMax-M3 (1M context) takes the DSA modules' long-context grid:
  batch x isl <= 16384 new tokens, batch x (isl + prefix) <= 1M KV
  tokens, the 13 prefix anchors, and from a 16K prefix on only isl 1,
  512, 2048, 8192 and 16384. 2048 is M3's knee too (16 top-k blocks of
  128). Decode keeps the shared grid and adds KV lengths 262144, 524288
  and 1048575 at batch 1, 2 and 4. The grid's YAML keys and sweep fields
  lose their dsa_ prefix, and the sparse-module expand and model rule
  take the attention type.
- Heads and model: MiniMax-M3's TP 1 to 16 shards (64 to 4 heads), as
  before.
- Precisions: the plan holds the union of the combinations the backends
  collected. skips.py entries keep each backend's set: TRT-LLM takes no
  FP8 KV cache, SGLang times BF16 projections only and no FP8 KV on SM89.
- TRT-LLM runs MSA off SM100/103 on its Triton reference, a dense FP32
  attention. A skips.py entry stops its context at a 32K prefix there,
  where a 16K chunk does no more work than the isl-32768 point collected
  before (18 s per forward on h200); a longer prefix runs out of memory.
  It replaces the 32768-token Triton budget.
- Rows: TRT-LLM queues one run_msa_module_series_worker task per module
  build, vLLM keeps one task per point (run_msa_module_worker takes the
  case), and SGLang rows carry their [batch, isl, prefix] points instead
  of a manifest the getter built.

The plan no longer depends on the device. SGLang refuses a point beyond
the prefill chunk it uses on the device (the collector no longer raises
the chunk to the largest planned shape) or beyond its KV pool, and vLLM a
decode point whose KV cache cannot fit, each with a FIXME(kernel-limit)
reason before allocating it. The AIC_MSA_* switches, SGLang's plan-time
memory filter and get_msa_head_counts are gone; the mla_module_trtllm and
mla_module_vllm sections, read only by the MSA getters, are deleted; and
the TRT-LLM, vLLM and SGLang module CLIs run the plan's rows. The rule
docs describe batch rows and the run-time refusal.

GPU-free comparison with the previous getters at SM 80 to 120:
- Each backend keeps its precision combinations on every SM. vLLM's FP8
  KV on SM80/86 now falls under the FP8 floor in skips.py; no MSA table
  exists there.
- Context points per SM90 / SM100 (TRT-LLM, vLLM, SGLang): 1,940 / 3,660
  -> 5,210 / 8,445; 4,880 / 7,320 -> 11,260 / 16,890; 35,000 -> 5,630.
  Decode: 1,840 / 2,760 -> 1,930 / 2,895; 3,680 / 5,520 -> 3,860 /
  5,790; 1,850 -> 1,930.
- TRT-LLM and vLLM lose prefix 128 and batch x isl beyond the 16K chunk,
  and gain prefixes to 1M (TRT-LLM to 32K on SM80-90 and SM120). SGLang
  loses the one-shot prefills beyond the chunk, its 26-value prefix list,
  batches 512 and 1024, isl 4 and 8 and decode KV 1, and gains isl 1536
  to 12288 and the long-KV decode points.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
… refuse

The DSA diag on h200 (pipeline 78355014) refused 22 points as planned:
prefills of more than the 8,192 new tokens SGLang chunks on that GPU.
The other 90 points were measured. But the failure was recorded as
unexpected rather than kernel_limit. Under torch.distributed, SGLang's
subprocess prefixes every output line with "[rank0]: ", so the parent
found no traceback to read the classified error from. The DSA/MLA and
MSA module runners now strip that prefix before looking.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
dsv4_{csa,hca}_{context,generation}_module now expand one plan for
TRT-LLM, vLLM and SGLang.

- Grid: context takes the DSA and MSA modules' long-context grid: batch
  x isl <= 16384 new tokens, batch x (isl + prefix) <= 1M KV tokens, the
  13 prefix anchors, which keep the 512 (DeepSeek-V4-Flash) and 1024
  (DeepSeek-V4-Pro) top-k knees, and from a 16K prefix on only isl 1,
  512, 2048, 8192 and 16384. Decode keeps the dsv4 block's batch and KV
  lists within the decode budget of cases/base_ops/dsv4_attention.yaml,
  to a 1M KV length at batch 1, now on every backend.
- Models and heads: the Flash and Pro FP8 checkpoints at TP 1, 2, 4 and
  8, as before.
- Precisions: the plan holds BF16 and FP8 block-scaled projections
  (module_gemm_types). skips.py keeps what each backend collected:
  TRT-LLM and vLLM time fp8_block only, and SGLang no fp8_block for a
  native checkpoint on SM90. The KV cache is FP8 on every backend and
  the rows name it; TRT-LLM picks the fp8_ds_mla pool on SM90 and the
  plain FP8 pool elsewhere.
- Rows: TRT-LLM queues one run_dsv4_attn_series_worker task per module
  build, vLLM keeps one task per point, and every SGLang row, decode too,
  carries its (prefix, seq_lens) points, so the subprocess never derives
  a grid.

The plan no longer depends on the device. SGLang's context memory filter
is gone: at 16384 new tokens the BF16 hidden state stays below 250 MB.
SGLang refuses a prefill beyond the chunk it derives on the device before
building the ModelRunner, and a point beyond its KV or SWA KV pool after
building it, each with a FIXME(kernel-limit) reason, and its subprocess
keeps that classification. Before, those points were skipped and the
task passed. The refusal helpers move from the SGLang MSA collector to
runtime_limits.py, and both collectors use them.

AIS_DSV4_KV_CACHE_DTYPE, AIC_VLLM_DSV4_MAX_SEQ_LEN, the --smoke argv
checks of the module getters and workers, the max_seq_len and
max_context_query_tokens budgets and the case_generator module getters
are gone. The SGLang CLI runs the plan's rows, and the module hash
closures name the plan's inputs.

GPU-free comparison with the previous getters at SM 80 to 120, per
attention kind:
- Each backend keeps its precision combinations on every SM, and
  SM80/86/89 plan nothing, as before.
- Context points on SM90 to SM120 (TRT-LLM, vLLM, SGLang): 5,840,
  5,840, 113,872 -> 4,504, 4,504, 9,008. TRT-LLM and vLLM lose isl 4
  and 8, batches 512 and 1024, batch x isl beyond 16K and the prefixes
  128 and 65536 - isl, and gain the prefix anchors to 1M. SGLang loses
  the one-shot prefills beyond the chunk, its 28-value prefix list, isl
  4 and 8 and batches 512 and 1024.
- Decode points: TRT-LLM and vLLM 1,544 -> 1,600 (KV lengths beyond
  65536 at small batch); SGLang 3,200, unchanged.
- Tasks: TRT-LLM 5,840 -> 72 context and 1,544 -> 88 decode; SGLang
  176 -> 144 context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
dsv4_paged_mqa_logits_module (SGLang, vLLM) and dsv4_csa_topk_calib
(SGLang) now expand op plans too.

- paged_mqa_logits: one grid for both backends, the dsv4 block's sparse
  lists vLLM timed (batch x isl <= 8192, batch x (isl + past KV) <= 1M,
  TP 1) for both default checkpoints. vLLM keeps one task per point, with
  the same rows and case IDs. SGLang times one row per (model, TP, batch)
  whose points the plan lists, instead of deriving them from the module
  sweep and the prefill chunk of the device.
- topk_calib: the CSA module plan's points of the calibrated checkpoint,
  expanded at plan time: the v1 top-k on the context points and the v2
  top-k on the decode points, one new token after the KV length. One
  SGLang row per batch.

SGLang's run_dsv4_sparse_kernel_worker takes the row's batch, TP and
[prefix, isl, variant] points, and writes the row's TP. The
case_generator sparse getters and lists, module_past_kv_lengths,
AIC_VLLM_DSV4_SPARSE_MAX_QUERY_TOKENS and the vLLM --smoke argv checks
are gone, and the sparse hash closure names the plans' inputs.

GPU-free comparison with the previous getters and SGLang's derived
shapes, per SM:
- paged_mqa_logits: vLLM 3,918, unchanged. SGLang 4,110 (SM90/120) and
  4,790 (SM100/103) -> 3,918: it loses past KV 10000 and 1000000 (184
  points), 8 decode shapes at exactly 1M KV tokens, and on SM100/103 the
  680 prefills beyond 8192 new tokens.
- topk_calib (SGLang): 2,251 (SM90/120) and 2,591 (SM100/103) -> 763:
  it loses the prefixes off the 13 anchors, isl 4 and 8, the isl that are
  not chunk-sized at a prefix of 16K or more and context batches 512 and
  1024. On SM90/120 it gains the 96 prefills of 8K to 16K new tokens the
  context plan holds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
moe_ep (SGLang and TRT-LLM wide-EP registries), deepep_ll and
deepep_normal now expand one plan each in collector/op_plans.py, and
their getters are the one-line plan call.

- moe_ep: one case per large-EP build, i.e. quant mode, routed-expert
  layer, EP size and EPLB layout, from the wideep: true MoE rows at TP 1
  of the shared grid. The plan holds the union of both backends' builds:
  fp8_block and nvfp4; EPLB off, on, and on with the 288-slot layout
  where it holds every expert; slots shard over EP.
  - TRT-LLM rows run one task per power-law draw of the shared grid, with
    the same 16 run arguments as before.
  - SGLang rows keep the 9 run arguments; SGLang still sweeps its own
    prefill and decode grid inside the task.
- skips.py entries keep each backend's builds:
  - SGLang times FP8-block only, without EPLB, on the checkpoint's own
    quant mode.
  - TRT-LLM times a build once across models: its synthetic weights
    ignore the model, and the table has no model column.
  - TRT-LLM's fp8_block on SM89 fails in the DeepGEMM compiler.
- deepep_ll and deepep_normal: one case per distinct
  (hidden_size, num_experts, topk) of the packaged MoE model configs, as
  before. Their shared shape reader moves into the plan, and rows keep
  the shape dict the full-node runners take.
- AIC_ACCURATE_WIDEEP_SIM is gone: TRT-LLM rows carry the accurate
  wide-EP simulation they always ran with by default.
- test_op_plans.py runs its per-getter checks over the wide-EP
  registries too. The hash closures of the four modules gain
  op_plans.py.

GPU-free comparison with the previous getters at SM 80 to 120:
- deepep_ll and deepep_normal: identical, 25 shapes on every SM.
- moe_ep SGLang: identical (62) on SM89 to SM120. SM80/86 drop to 0
  under the shared FP8 floor; FP8 needs SM89 and DeepGEMM SM90.
- moe_ep TRT-LLM: the same rows (0 / 224 / 448 on SM89 / SM90 /
  SM100+). Within a model, quant mode and EP size, they now run layout
  before draw; the case plan hash does not depend on order.
- Case IDs are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
moe_expert_compute_perf has no model column, so moe_ep builds of
different models that write the same key are now one case in the plan.
The case is owned by the first model, in plan order, whose checkpoint
plans the quant mode, or by the first model when none does; SGLang's
own_quant entry still leaves those out. This replaces the repeated_build
field and its TRT-LLM skips.py entry, so the dedup lives in the plan.

GPU-free comparison with the previous commit at SM 80 to 120:
- SGLang: 62 -> 46 on SM89 to SM120. The 16 rows dropped are the
  GLM-5.1-FP8 and GLM-5.2-FP8 builds at EP 2 to 256, whose keys
  GLM-5-FP8 writes with the same SGLang routing fields.
- TRT-LLM: the same 224 / 448 runtime identities. 112 rows on SM90
  and 294 on SM100/103/120 now name the checkpoint that plans the
  quant mode (for example zai-org/GLM-5-FP8 rather than
  nvidia/GLM-5-NVFP4 for fp8_block), so their case IDs change.
- deepep_ll and deepep_normal: unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
vLLM takes FP8 prefill queries only on device capability family 100
(backend_supports_prefill_query_quantization in
vllm/model_executor/layers/attention/mla_attention.py). On SM120 it falls
back to BF16 queries, which the collector refuses to record as FP8, so the
FP8-compute mla_context_module cases queued there failed on every run; the
shipped rtx_pro_6000_server tables hold no FP8-compute rows. The skips.py
entry that covered SM89 and SM90 now covers SM120 too.

GPU-free: vLLM mla_context_module on SM120 goes from 972 to 648 cases;
every other op, backend and SM is unchanged. The SDK resolves FMHA from the
data it finds, so with no FP8 slice on SM120 it already falls back to BF16
(or rejects an explicit FP8 request).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
Nothing read the per-module granularity of collector_hash. Full CI turned
it into a changed-operation manifest that it uploaded and nothing consumed
once the evidence chain was removed; the collection harness does not read
it; the SDK only checks that the field is a non-empty string. Keeping it
cost an authored closure per module in hash_closures.yaml, and a missing
entry was easy to miss.

- collector_hash is one content hash per source tree: every .py, .yaml,
  .yml, .sh and .patch file under collector/, plus the SDK Python and the
  packaged model configs the runners import, skipping __pycache__. JSON
  under collector/ is left out because the TRT-LLM MoE autotuners write
  their caches there. provenance.hashed_files lists the covered paths.
- collection_meta.yaml keeps its schema: collector_ref, collector_hash and
  case_plan_hash stay.
- hash_closures.yaml, the closure loader and module enumeration in
  provenance.py, tools/perf_database/changed_ops.py with its tests, and the
  Full CI changed-ops steps with the comparison base they needed are gone.
- Tests that pinned closure membership now check that the files a runner
  depends on are hashed, or that editing them changes the hash.

Every table collected from one tree now carries the same collector_hash.
The suffix-less wideep/vllm host_tools/ibstat shim is the one closure file
the hash no longer covers; collector_ref still identifies it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
The six deviation notes lived in collector/notes/<backend>.yaml under a
closed schema that check_collector_data.py R8 validated, failing a note
whose verified_on fell behind a pin. Like the reasons in collector/skips.py,
each is now a comment at the code that measures the deviating path:

- SGLang GEMM (bf16 CuTe DSL dispatch, nvfp4 activation quant, fp8_block
  Triton fallback): collector/sglang/collect_gemm.py.
- TRT-LLM unfused mHC on SM100/103: collector/trtllm/collect_mhc_module.py;
  the four mHC reuse.yaml comments point there.
- vLLM standalone compute_scale under torch.compile:
  collector/vllm/collect_computescale.py.
- vLLM fp8_static derived from the dynamic row: the fp8 branch of
  collector/vllm/collect_gemm.py.

Each comment keeps what differs and where, the direction of the error, its
sources and how to retire it, and drops verified_on, approved_by and version
pins. The notes files, R8 and its tests are gone, so check_collector_data.py
runs R1-R7 again. The rules, docs, skills and CodeRabbit instructions
describe a note as that comment; a pin bump re-checks the notes in scope in
review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
glm5_mqa_logits_module, glm5_topk_module and glm5_dsa_attn_module (SGLang)
now expand one op plan each, and their getters are the one-line plan call.

- Points: the DSA module plan's points of the checkpoint (the shared
  long-context grid), context as (batch, isl, prefix) and decode as one new
  token over its KV length. Before, the worker derived its own shapes from
  the SGLang module sweep; rows now carry them: one
  run_glm5_dsa_sparse_kernel_worker task per (model, batch) with its
  [prefix, isl] points, shortest prefix first.
- Model: the case carries the checkpoint. The plan holds the BF16 GLM-5 and
  the first declared NVFP4 checkpoint (GLM-5.2-NVFP4), and two commented
  skips.py entries keep today's choice: GLM-5 on Hopper, GLM-5.2-NVFP4 on
  SM100/103/120. A targeted run (COLLECTOR_MODEL_PATH) gets the same
  checkpoints as before on every SM.
- Removed with the old derivation: the --smoke sampling, the shape helpers
  only this getter used (_dsa_context_prefix_shape_is_valid,
  _DSA_CEILING_MAX_POSITIONS, dsa_indexer_total_kv_tokens_supported,
  _derive_context_shapes), and dsv32_dsa_sparse_modules.py, an unregistered
  DeepSeek-V3.2 wrapper with no table or consumer.

GPU-free comparison with the previous getter (points per kernel; the three
kernels share them):
- SM90 (GLM-5): 3,026 -> 672; SM100/103/120 (GLM-5.2-NVFP4): 3,333 -> 692;
  SM80/86/89 plan nothing, before and after. Tasks: 11 per SM, as before.
- Lost (SM90 / SM100): context prefixes off the 13 anchors, incl. the
  max-position ceilings (1,168 / 1,194); batch x isl above 16,384 (511 /
  558); batch x (isl + prefix) above 1M (253 / 448); context batches 512
  and 1024 (280 each); isl 4 and 8 (138 / 144); non-chunk isl at a 16K+
  prefix (119 / 140); decode KV 1 and the 163,839 / 202,751 ceilings (13).
- Gained: isl 1,536 to 12,288 (66); decode batch x KV above 262,144 up to
  the module plan's budget (42 / 49); decode KV 2 and the long-KV points
  (11 / 12); a one-token prefill without prefix per context batch (9).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
dsv41_module (SGLang) now expands one op plan, and its getter is the
one-line plan call. A case is (checkpoint, TP, execution profile), from the
model profile's tensor_parallel_sizes and execution_profiles; its row is
the native runner's existing {id, params: [model, tp, profile, sweep]}.

DSV41_EXECUTION_PROFILE is gone. The two profiles still write separate
tables, so a run collects one of them with a case filter on the row id
(--case-filter dsv41_full_ or dsv41_decoder_bounded_); the locked output
marker still refuses a second profile on one table.

GPU-free comparison with the previous getter at SM 80 to 120: the same 8
rows on every SM, in the same order, with the same ids and parameters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
…ps.py

dsv4_csa_topk_calib took its checkpoints from dsv4.calib_model_paths, a
collection-cost policy that dropped DeepSeek-V4-Pro from default plans with a
log line and let a targeted run collect any model. The plan now calibrates
every planned DeepSeek-V4 checkpoint, and one commented skips.py entry
leaves sgl-project/DeepSeek-V4-Pro-FP8 out. calib_model_paths, its
validation and get_dsv4_models(calib=...) are gone.

Default plans are unchanged: the same 11 rows on SM90 to SM120, none below.
A targeted run on sgl-project/DeepSeek-V4-Pro-FP8 no longer calibrates Pro;
one on deepseek-ai/DeepSeek-V4-Pro still does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
… time

vLLM's XPU attention getters were the last to drop cases with a
generation-time memory filter: a case whose bf16 KV pool (8192 blocks of 64
tokens) exceeds 0.45 of the device was never queued, so the case list
depended on the device. The getters now queue every case, and
run_attention_torch refuses such a case before allocating it, with a
"FIXME(kernel-limit): does not fit" reason the failure log classifies as a
kernel limit.

GPU-free: the getters now return 14,294 context and 24,532 generation
cases on any device; before, a 24 GB device queued 13,946 and 24,044 and a
48 GB one 14,120 and 24,300. Those differences are now run-time refusals.

No getter filters on memory any more, so the collector rules replace "the
one sanctioned in-collector filter" with the run-time capacity refusal
(layer_permissions.md, and its mentions in failure_handling.md,
case_authoring.md, the op-development skill and the CodeRabbit
instructions).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
…ps.py

Some runtime FIXME(kernel-limit) raises depended only on case fields and
the SM, so every case they covered failed on every run after being queued.
Each becomes a commented skips.py entry, and its raise goes:

- TRT-LLM MLA (mla_context, mla_generation and both MLA modules) below
  Hopper: AttentionOp asserts there. The two runners keep an assert
  for direct runs, which would otherwise abort.
- TRT-LLM MLA decode on SM100/103 with more than 16 heads per rank that
  are not a multiple of 16 (numHeadsPerCta=16). MlaCase gains local_heads.
- TRT-LLM and vLLM DSA modules on SM100/103 with fewer than 8 heads per
  rank (trtllm-gen sparse-MLA tiles).
- vLLM DSA modules on SM100/103 with an FP8 KV cache and 64 < heads per
  rank < 128 (FLASHINFER_MLA_SPARSE has no kernel). No planned DSA
  checkpoint shards to such a head count today.
- TRT-LLM wide-EP fp8_block off Hopper (DeepGEMM's JIT compiles for SM90
  only); the existing SM89 entry now also names SM100, SM103 and SM120.

Queued rows, before -> after:
- mla_context / mla_generation, TRT-LLM: SM80/86 1,540 / 2,534 -> 0,
  SM89 3,080 / 5,068 -> 0; mla_generation SM100/103 5,068 -> 4,706.
- mla_context_module / mla_generation_module, TRT-LLM: SM80/86 108 / 132
  -> 0, SM89 432 / 528 -> 0.
- dsa_context_module / dsa_generation_module, TRT-LLM and vLLM, SM100/103:
  540 / 660 -> 486 / 594 series rows.
- moe_ep, TRT-LLM, SM100/103/120: 448 -> 224.
No other op, backend or SM changes.

Left at run time:
- the per-shape MoE alignment checks (SGLang and TRT-LLM collect_moe), which
  fail single shapes, not blocks;
- SGLang's trtllm_mla decode head range, whose backend depends on the
  image's CUDA version;
- vLLM's MiniMax-M3 decode scorer check, which no planned head count reaches;
- the "does not fit" capacity refusals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
…n base

Collector Data no longer fetches a comparison base: it only fed the
changed-operation manifest, which "hash the collector source once"
removed together with the base_sha input of collector-check.yml. Three
workflow contract tests still asserted the fetch and the input, so the
Repository Policy job failed:

- test_migrated_workflows_keep_reviewed_safety_fixes drops its
  BASE_SHA fetch assertion and keeps the --depth=1 one;
- _run_comparison_base checks only prediction-regression, the one
  remaining consumer of verify-target's comparison-base output;
- test_collector_comparison_fetch_preserves_full_history is removed
  with the fetch it guarded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
…mla pool

An RTX PRO 6000 run of the TRT-LLM DSA modules without the SM120 raise
(1.3.0rc29, pipeline 78388970) failed every point of the seven selected
tasks: 109 points of DeepSeek-V3.2 and GLM-5.2, BF16 and FP8 KV, context
and generation. Each failure was TrtllmAttention's "DeepSeek-V4/DSA sparse
MLA on SM120/SM121 requires kv_cache_config.dtype='fp8_ds_mla'"
(attention/backends/trtllm.py). The DeepGEMM "Unsupported architecture"
assert that the raise cited no longer fires, because rc29's DeepGEMM
dispatches SM120.

The same check covers DeepSeek-V4. Its attention inherits MLA, which hands
extra_attrs["kv_cache_dtype"] (default "auto") to the backend, and
collect_dsv4_attn sets fp8_ds_mla on SM90 only.

- collector/skips.py leaves out TRT-LLM's DSA modules and the four
  DeepSeek-V4 attention modules on SM120, with the reason. The SM90 FP8-KV
  DSA entry drops its stale SM120 sentence.
- run_mla_module loses the SM120 raise and its two FIXME comments.

Queued on TRT-LLM SM120, before -> after (series rows / points):
- dsa_context_module 540 / 33,330 -> 0; dsa_generation_module 660 / 11,310 -> 0;
- dsv4_csa_context_module and dsv4_hca_context_module 72 / 4,504 -> 0 each;
- dsv4_csa_generation_module and dsv4_hca_generation_module 88 / 1,600 -> 0
  each.
No other op, backend or SM changes. No shipped table has TRT-LLM rows for
these ops on SM120.

Timing them on the fp8_ds_mla pool, as serving does, is a follow-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
dsv4_csa_topk_calib now plans every default DeepSeek-V4 checkpoint: skips.py
no longer leaves sgl-project/DeepSeek-V4-Pro-FP8 out. Consumers key the top-k
DELTA per native head geometry (128 heads for Pro, 64 for Flash), so Pro rows
land in their own bucket and never replace Flash's. Until a collection ships
them, Pro predictions keep running without the top-k correction.

Default SGLang plans on SM90 to SM120 grow from 11 tasks and 763 points to 22
tasks and 1,526 points; plans below SM90 stay empty.

A GPU smoke on SGLang 0.5.21 ran Pro's batch-1 and batch-1,024 tasks (168
points, up to a 1,048,575-token prefix and a 16,384-token prefill) on h200 and
b200, one GPU each (pipelines 78444833 and 78444839): 336 rows per GPU, no
failures. Rows are zero exactly where the compressed KV fits within Pro's
top-k of 1,024 entries. At the points Flash's tables share, the non-zero values
match Flash's: median ratio 1.01 on h200 and 1.00 on b200.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
The collector rules, skills and review instructions now describe the
collector as it is: every op except moe_a2a is on an op plan, and
collector/skips.py is the one place that leaves cases out.

- overview.md:
  - the five things, with the Record now carrying one collector_hash
    and the case_plan_hash;
  - the failure principle;
  - the rules and their checkers;
  - an open-gaps table (moe_a2a, the vLLM XPU getters, Record counts,
    the consumer signal) in place of the migration table.
- layer_permissions.md:
  - one "Leaving cases out" section holds the four reasons a case may
    be skipped;
  - the capacity, kernel-selection, serving-parity, deviation, version
    and module-boundary rules keep their substance;
  - source citations name file and symbol;
  - every re-check at a pin bump is listed once, in the version
    contract.
- failure_handling.md and case_authoring.md point to those sections
  instead of restating them:
  - the op-plan section covers dispatch, rows and batch rows;
  - the stale "tp=4" filter example is dropped.
- The collector op-development and auto-collect skills, the silicon
  alignment skill, AGENTS.md and both CodeRabbit configurations drop
  quant-axis min_sm floors, in-collector skips, capability floors and
  generation-time memory filters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
Comments, docstrings and messages under collector/, tools/perf_database/
and the collector tests, rules and skills no longer narrate history:

- Source citations name the file and symbol, or file:line, without a
  framework version: `file.py:12@1.3.0rc20` becomes `file.py:12`.
- Decision dates, internal PR or issue numbers and most observation
  dates are gone, including the "owner policy" date on the one-release-
  per-file header of 22 collectors.
- "Re-verify on the next bump" sentences are gone; the version contract
  in layer_permissions.md has every pin bump re-check citations, skips
  and notes.
- A few history-only passages now state the current fact instead: the
  TRT-LLM MLA module header, the vLLM block-FP8 dispatch note, the SGLang
  Llama-4 router note and the all-reduce capture note.
- Stale "capability floor" references point at collector/skips.py.

Kept on purpose:
- functional version strings: framework pins, __compat__, manifest
  versions, docker digests, data paths and version comparisons in code;
- framework_manifest.yaml's retired-pin notes, which explain shipped
  tables;
- the source fields of kernel_source_backends.yaml, which record where a
  label came from;
- citations of upstream framework PRs (FlashInfer, vLLM, SGLang,
  TensorRT-LLM) that serve as sources;
- FIXME(re-verify) comments, whose versions name the open question.

Left for a follow-up: 62 comment lines in 25 collector files still date a
source audit or a hardware observation. An example is the SGLang
0.5.14/0.5.17 audit at the top of sglang/collect_gemm.py. Several still
explain how older shipped tables were collected, so each passage has to be
read against the data before it is rewritten.

No behaviour changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tianhao Xu <tianhaox@nvidia.com>
@tianhaox
tianhaox force-pushed the feat/collector-v4-gemm-plan branch from f3a9a27 to 4d8ecf2 Compare November 2, 2026 01:54

This branch has not been deployed

No deployments
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.

1 participant