Skip to content

[Model] Qualify calibrated E4M3 and disk PLE with QSA DCP2 - #702

Draft
yangzhuxinyzx wants to merge 49 commits into
mainfrom
codex/qwen38-dcp696-integration-20260927
Draft

yangzhuxinyzx wants to merge 49 commits into
mainfrom
codex/qwen38-dcp696-integration-20260927

Conversation

@yangzhuxinyzx

@yangzhuxinyzx yangzhuxinyzx commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

Follow-up qualification/integration of #696 (DCP2) and #701 (high-precision
reductions / host PLE placement). Draft: paired dataset acceptance and DCP2
long-context model checks pending.

This does not implement a competing DCP layout and does not supersede #696.
Both original histories are preserved. The distinct changes are safe E4M3
defaults for SM70 Qwen3.8 NVFP4 TP4/MTP0, admitting the existing disk-PLE
producer under DCP2, reconciling current-main GDN/prefix semantics, real
four-rank collective/graph regression tests, and a source-installed bounded
model/256K quality driver. The original PRs remain dependencies/review scopes.

  • Integration base: b034648012244ab712df05b93e6d8fff877a6f2f.
  • [Model][QSA] Target-only DCP2 for Qwen4Exp QSA on TP4 V100 #696 imported head: 9e4236c2a6f5a15c44c1a0542ab53a779eade730.
  • [Core] Keep CUDA reductions and PLE memory on safe defaults #701 prerequisite: 71c7de134e9107655ad01a1e329f0813c8b0c1b8.
  • Integration/follow-up commits: fd4b545e0c, 32acc8bda7, f6c62234e9.
  • Benchmark/acceptance head: dd7624462fae2b7016aaa8843527e9fc3af85b67.
  • Initial health/capacity runtime source:
    f6c62234e9959edb5ea2fda2b386ee0cd71d276b.
  • Main 1e90d17f2c merged cleanly at d6ff5be488; normal new native build and
    installed wheel prepared. Current-main full-model dataset acceptance pending.

Automatic E4M3 applies only to the admitted no-MTP route; explicit FP16 wins.
Automatic selection turns on strict calibration checks unless overridden.
The published revision-bound 24-scale payload matches our base index and has
been materialized into an independent model view; original weights unchanged.
Compute reductions remain FP32 / high-precision. FP8 is main KV storage only.

Test Plan

  • Operator math and packed cache ownership first, without loading weights.
  • Actual four-process TP4/DCP2 NCCL collectives and graph replay, not mocked
    communication.
  • Preserve GDN state metadata, MTP group-specific prefix rules and PLE lifecycle.
  • Rebuild ordinary native package, then use clean installed extensions without
    private DSOs or LD_PRELOAD.
  • DCP1/2 same-contract calibrated E4M3 model runs: natural-EOS output health,
    token differences as diagnostics, 8K/32K/near-256K retrieval and exact 262143+1 boundary,
    allocator/worker memory, separate prefill/decode metrics. Shut workers down.
  • Per the user's acceptance criterion, rounding/reduction-order differences are
    permitted if paired task quality does not regress. First screen: fixed
    128 GSM8K + 32 LongBench multifieldqa_en + 32 multifieldqa_zh; identical model,
    official sampling and per-question seeds; report per-dataset scores and paired
    wins/losses, truncation, empty output and nonfinite logprobs. This finite subset
    is not proof of universally lossless quality. Keep high-precision reductions.

Test Result

  • 98 passed: DCP indices/attention/local-width/packed-cache, GDN row selection,
    graph-padding metadata tests.
  • 213/214 initial cache/GDN/config cases passed. Corrected one stale test using
    three-tuple unpacking of the new four-field SpecGroup; targeted rerun passed.
  • 35 PLE/cache tests passed, including that corrected cache case.
  • tests/models/qwen4_exp/test_qsa_dcp_collectives.py: passed on four V100s;
    FP16/E4M3, rows1/5/33, AG+RS and A2A, eager and two changed-input graph
    replays, checked against an independent FP32 oracle.
  • 11 selected allocator/offload regressions passed.
  • High-precision reduction policy test passed; 87 combined default/PLE tests
    passed after [Core] Keep CUDA reductions and PLE memory on safe defaults #701, including environment-isolation repair.
  • Pre-commit including mypy, formatting, SPDX and CUDA API checks passed.
  • 12 long-address FP16/E4M3 boundary/operator cases passed.
  • 14 driver/dataset CPU tests passed. Actual tokenizer/scorer preflight passed for
    all 192 selected questions (100–13,601 input tokens); local GSM8K rows exactly
    match a pinned official source after JSON normalization.
  • The offline driver now explicitly enables the native sampler NaN checks and
    verifies each worker's sampler setting. The previous full-model short health
    cases checked natural EOS/answers but did not opt into full-logit NaN scanning;
    their exact-boundary sampled logprob finite check remains valid. NaN auditing
    disables the greedy-only shortcut, so these health timings are not production
    throughput results.
  • Source-complete wheel built and installed into a fresh runtime with normal
    CUDA/Torch dependencies only; no private DSO, preload or source overlay.
    Wheel SHA256:
    e1cb69363ff7e1ff66c2fc04f25564f6d52bb17cc9f8d278177f9e9fd29460be.
  • DCP1 full-model health: all seven arithmetic/copy/retrieval cases passed with
    natural EOS, including 261,632-token input. Exact 262,143+1 boundary returned
    finite logprob. This is health evidence, not a dataset accuracy result.
  • DCP2 initial arithmetic cases returned the same final answer. Its greedy
    reasoning first diverged at zero-based token 27 and stopped the old parity
    gate before long tests. Under the updated criterion this is diagnostic, not
    by itself a dataset-quality regression.
  • Model-free same-KV DCP1/DCP2 attention comparison: decode max absolute
    difference 2.44e-4; short-prefill 9.77e-4. DCP2 was not farther from the FP64
    oracle in three prefill seeds. This does not establish the exact cause of
    the full-model token difference.

Same 4 GiB/card physical KV budget, TP4, E4M3:

Measured metric DCP1 DCP2
Physical KV bytes/card 4,291,854,336 4,293,058,560
Allocator token capacity 600,052 1,061,981
Startup allocated GiB/card 25.414 25.567

Allocator capacity increases 76.98%; four simultaneous 256K requests have
not been tested. PLE logs confirm GPU table rows=0, disk mmap prefill and
pinned-host/UVA decode. Runtime arithmetic precision flags remain disabled
for reduced-precision reductions/accumulation.

Commands and the bounded model contract are in
docs/design/qwen38_calibrated_dcp_integration.md.
This PR does not claim restored 97 tok/s, paired dataset acceptance or
accepted DCP2 256K model quality yet. #696 author's measurements are not ours.
Raw local evidence is retained in the owned worktree's .artifacts/:
model_dcp1.json, model_dcp2.json, dcp_numerics.log,
dataset_preflight.json, dataset_driver_cpu_tests.log and WORKLOG.md.
Dataset screen correction: the first DCP1 192-question run accidentally
selected source-checkout workers: the reused GSM8K benchmark indirectly
prepended source paths to sys.path. Concurrent source/build changes invalidate
it as a frozen-artifact comparison. Its scores are retained as diagnostic only;
they are not promotion evidence. Earlier non-dataset health/capacity worker
manifests confirm installed-wheel paths and remain valid.

The scorer no longer imports benchmark entrypoints. A real CPU spawn now
verifies package/native-extension paths and version before model load, then
actual worker manifests must match. 20 CPU regression cases and the actual
192-question/tokenizer/scorer/spawn preflight pass with the current-main wheel.

The first DCP2 dataset attempt failed the pre-load memory check (28.15 GiB free,
28.56 GiB requested), before output evaluation. Exact transient memory ownership
was not recorded; do not attribute this to another task or DCP numerics.
All owned GPU processes exited. Other tasks now hold both four-card groups.
The corrected pair uses the same utilization0.85 on both sides with the
unchanged explicit4GiB/card KV budget and preserved precision/context.

Updated source-installed wheel SHA256:
3687562a695ccc2052059eb503aefce705e6a72555a324173cc8c9feac0ec091.
65 targeted CPU warmup/graph tests passed after current-main integration.
Corrected full-model dataset pair has not started; PR remains Draft.

Review / Risks

AI assistance was used (Codex). Human line-by-line review and relevant reruns
are still required before promotion/merge. No direct main push or running
service is part of this change. DCP4, automatic MTP enablement, and a lower
arithmetic-precision speed path are out of scope. The default now requires a
calibrated model view; --kv-cache-dtype float16 is the explicit control.

areslp and others added 30 commits September 19, 2026 00:49
…3 NaN

The SM70 grouped-page4 QSA prefill route can emit NaN for every query row
and head on a fixed set of head dims when the KV cache is E4M3 and a hybrid
(GDN/Mamba) layout co-locates non-attention state in the paged KV pool. The
same batch is correct through the XQA route.

Root cause: the grouped planner pads each category to a multiple of eight
with the null block -- (physical microblock 0, mask 0) -- and counts the
padding in seq_len. The forward loads page 0's K/V for those padded rows and
masks them multiplicatively (P = 0). But 0 * NaN = NaN survives the P@V
tensor-core reduction, and under E4M3 the null block's bytes decode to NaN.
Every group's every tile reads the same page-0 microblock, so the whole
tile's rows and heads go NaN on identical dims. XQA never reads the null
block, which is why it stays clean on the same inputs.

Fix (defense in depth, both default-on):
- Kernel: in flash_attention_grouped_verify_e5m2_partial_kernel under
  SPARSE_PAGE4, after loading each K/V panel, zero every row no query
  attends (unattended iff (mask & (0x11111111u << (row & 3))) == 0) so the
  reduction sees 0 * 0 = 0. A leading __syncthreads() before the V-panel
  zeroing keeps the panel load from racing past the zero and resurrecting
  the NaN; the trailing one publishes the zeros before P@V.
- Python (VLLM_SM70_QSA_GROUPED_PAD_FIX, default 1): after the planner,
  repoint every mask==0 padding entry at the group's first real microblock.
  torch.where + copy_ has no host sync, so it stays CUDA-graph capturable.
  This backstops binaries built before the kernel fix.

The grouped route stays default-on (VLLM_SM70_QSA_GROUPED_PAGE4=1); only what
the padded rows contribute changes.

Adds tests/models/qwen4_exp/test_qsa_e4m3.py::test_qsa_grouped_page4_null_block_padding_no_nan,
an SM70-only regression that poisons the null block's value plane with E4M3
NaN and asserts the grouped route stays finite and matches XQA, parametrized
over the Python pad-fix off (kernel-only) and on. It reproduces the NaN on an
unfixed kernel and passes on either fix.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit cab8358)
…adding test

The regression added in the previous commit used the XQA page4 route as its
clean reference. On Flash-V100 builds whose XQA E4M3 page4 kernel requires an
fp32 partition buffer, that reference raises "XQA decode tmp_out must be fp32
for E4M3 KV" before the grouped route is exercised, so the test cannot run
against such a build.

Use a kernel-independent einsum ground truth instead: decode the E4M3 K/V to
fp32 and attend only the selected tokens (which map to physical pages >= 1, so
the null block is excluded). This is .so-agnostic and directly checks that the
grouped route stays finite and numerically correct. The grouped route and the
parametrized pad-fix coverage are unchanged.

Verified on V100 (SM70): the grouped null-block NaN reproduces with the stock
kernel and pad-fix off (test fails), and passes with either the kernel fix or
the Python pad-fix.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 31d137c)
Adapt vllm-project/vllm#51865 (0a94d85a66499cad8297ead86a470967de5c0212)
to the current target and Eagle/MTP draft paths. A K+1-token prompt tail
must not replay a decode graph merely because its shape matches.
Preserve dummy capture and genuine decode classification.

Co-authored-by: Rahul Chalamala <22563365+rchalamala@users.noreply.github.com>
Co-authored-by: Nick Hill <nickhill123@gmail.com>
Co-authored-by: Janelle Cai <janelle.cai@modal.com>
Assisted-by: OpenAI Codex
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
(cherry picked from commit 1892fb2)
Staging branch for the separate E4M3 + MTP draft-scale enablement; NOT for
merge and no PR. Ports two work-in-progress patch sets on top of the
null-block-padding fix branch:

- phase2: opt-in gate (VLLM_QWEN4EXP_QSA_E4M3_MTP) that relaxes the QSA E4M3
  MTP0 requirement, an extracted _verify_e4m3_kv_requirements, the envs entry,
  an MTP weight-remap hook, calibration-overlay tooling, and CPU/GPU tests.
- phase2b: a CUDA-graph-capture-safe XQA workspace (torch.full on-device
  instead of a host torch.tensor copy) with capture/envelope tests.

CPU check (CUDA hidden): 27 passed, 19 skipped (GPU-only), 2 failed. The two
failures (test_validate_scale_overlay_lists_missing_names,
test_finalize_qsa_scale_load_success_and_missing) exercise draft-side q-scale
visibility whose model.py scale-load changes are not part of these two
patches; that piece is out of scope for this staging branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 464f538)
Finish the staged draft-scale path without changing the existing main-model fallback contract. Add a repository-owned, revision-checked target/draft scale overlay materializer and make its CPU tests portable.

Co-authored-by: OpenAI Codex <noreply@openai.com>

Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
(cherry picked from commit bb4bc78)
The #647 overlay kept the stricter native E4M3 ABI but lost the matching Python workspace dtype selection. Key the cached workspace by KV dtype and allocate FP32 temporary output for E4M3 while retaining FP16 for the existing path.

Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
(cherry picked from commit b86e641)
ruff-format and clang-format only; no behaviour change. Keeps the native
tree byte-identical to the already-qualified SM70 build so its compiled
binaries can be reused.

Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
test_qsa_e4m3_loader_requires_all_24_scales asserted that an incomplete
overlay raises, but left VLLM_QWEN4EXP_QSA_E4M3_STRICT_SCALES unset, so
_validate_qsa_e4m3_scale_load took the non-strict warning path and the test
failed on main. The gate it claimed to cover was never exercised.

Set the env explicitly and assert both contracts: non-strict returns the
missing scale names so the caller can fall back to unit scales, strict
raises with the loaded/required count. Also assert the non-FP8 cache short
circuit returns an empty set rather than only that it does not raise.

Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Baseline for the isolated QSA DCP2 experiment. Includes Leon PR #19 and 1CatAI PR #617/#598 via the validated CT252 integration branch b4fef53.

Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Include the existing graph-padded token-count regression before changing QSA metadata for DCP2.

Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Prepare the experimental target-sharded, draft-replicated layout without enabling QSA DCP serving. Track per-layer physical pages and pad shared Mamba views to the actual tensor stride. CPU allocation and real worker reshape tests cover the mixed layout. Ownership design adapted from vllm-project/vllm PRs #56723 and #57431.

Assisted-by: OpenAI Codex

Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Preserve the token-ID/-1 selection ABI and the original MTP buffer. Expose optional base-2 LSE and FP32 partial output for cross-rank merging, with the output gate deferred until after the merge. V100 tests pass for localization, graph replay, FP16/E4M3 G12 attention and the original DCP1 call. This remains an operator prototype; serving DCP is still gated off.

Index localization adapted from vllm-project/vllm PR #57431.

Assisted-by: OpenAI Codex

Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Scale target attention blocks by DCP while keeping replicated draft and recurrent owners on their own token grids. Preserve worker physical specs and normalize only prefix lookup specs. Match grouped CPU budgets between worker and scheduler layouts. CPU tests exercise prefix reuse, shared page strides and the full target-only capacity projection.

Assisted-by: OpenAI Codex

Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Assisted-by: OpenAI Codex

Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Assisted-by: OpenAI Codex

Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
qsa_dcp_block_geometry declares a replicated selector page as the global
token span it covers, but the group's common block table enumerates the
kernel blocks this rank owns. _canonical_block_table divided the global
span by the local kernel block, doubling the virtual expansion under DCP2:
the canonical table collapsed from 11 columns to 6 and every page ID was
divided by twice the correct factor, mapping the table onto page 0.

The QSA slot kernel then found no column for the newest compressed groups
and wrote PAD_SLOT_ID for all 392 group boundaries of a 1568-token prefill
chunk, so the selector cache never received the current keys and the
indexer scored an identical query against a cache missing them. A
30,030-token prompt answered "blue" under DCP2 and "Blue" under DCP1.

Condition the existing division on whether the table counts local tokens,
which is true for a sharded page and equally for a replicated selector
sharing a sharded main owner's table. A standalone MTP draft replicates
every QSA cache and reads a global table, so it is unchanged, and DCP1 is
unaffected because the guard still requires dcp_world_size > 1.

This supersedes cc026e7, which cleared a startup failure by removing
the division rather than correcting its span, and replaces the test that
encoded that behaviour.

Verified on TP4/V100: DCP2 now returns Blue for the fixed 30,030-token
prompt, the canonical table is 11 columns, and the valid compressed-slot
positions match DCP1 exactly (392/1568, stride 4) where they were 0/1568
before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Signed-off-by: Leon <tomsel1010.us2@gmail.com>
get_dcp_local_seq_lens built its rank offset with
torch.tensor([[dcp_rank]], device=seq_lens.device). Creating a CUDA tensor
from a Python list is a pageable H2D copy that synchronizes the stream,
and every DCP metadata build calls this function, so each call waited for
the previous step's graph replay to drain. With MTP3 that is four builds
per decode step, and the stall broke CPU/GPU overlap: the GPU sat idle
while the CPU prepared the rest of the step, and the slowest rank's delay
propagated to every rank through the tensor-parallel all-reduce.

A TP4/DCP2 V100 profile attributed 99% of blocked host time (52 of 305
synchronizations, about 375 ms per rank over one 38-token request) to
this call site. Use the Python scalar directly in the broadcast, as
upstream vLLM already does, while keeping this tree's return shapes.

Measured on the same Qwen4Exp TP4/DCP2 configuration, median of five
streamed samples: output speed 57.5 -> 69.2 tok/s on a short prompt and
55.9 -> 68.5 tok/s at 8K (DCP1: 73.1 and 72.4). TTFT is unchanged. The
fixed-sample token and MTP acceptance gates remain identical to DCP1.

The new test checks every rank against a per-position reference and runs
the rank-scalar path under CUDA sync debug mode, which fails on the
previous implementation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Three changes to the DCP path of the QSA target layers, measured on
TP4/DCP2 V100 decode (per layer, 4 query rows):

- Narrow the localized selection. With interleave 1 every compressed group
  of 4 positions splits evenly over the DCP ranks, so a rank owns at most
  compress_ratio // dcp positions of every group and of the open tail.
  Compaction never writes past (topk // ratio + 1) * (ratio // dcp)
  columns (1026 of 2051 at DCP2), and the sparse kernel iterates every
  column it is given. Passing only that prefix: 96.9 -> 61.2 us.
- Apply the output gate with the Triton kernel the DCP1 page4 route uses
  after rounding the merged output to its dtype, instead of a chain of
  five elementwise kernels: 12.2 -> 1.4 us.
- Default Qwen4Exp DCP to the a2a combine (one all-to-all instead of
  all-gather LSE + reduce-scatter output): 31.9 -> 19.9 us. The option
  is now unset by default so a model can supply its preference through
  ParallelConfig.set_dcp_defaults(); an explicit user choice still wins
  and every other model keeps ag_rs.

End to end (C1, MTP3, E4M3, graphs, 32K), decode relative to DCP1 moves
from -5.4% to -3.4%. Token outputs and MTP acceptance of the fixed
accuracy gate (six MTP prompts, five code repeats, 30K marked prompt
with prefix hit/changed suffix) stay identical to DCP1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Leon and others added 18 commits September 25, 2026 23:55
prepare_attn reads input_batch.is_dummy_batch since the new runner marks
dummy QSA batches (bdbe40a), but this fixture's input batch never gained
the field, so the test failed with an AttributeError on every run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Every GDN cache group built its spec-decode metadata on every step by
uploading the pageable CPU row mask (a stream sync per upload) and indexing
device tensors with it (a nonzero sync per index): about a dozen host syncs
per group and step, plus an unused query-length upload and a device-side
assert. In steady MTP decode the speculative rows lead the batch, with graph
padding and any non-speculative rows after them, so:

- the state contract selects rows by slicing (cloned, so still fresh
  contiguous tensors) when they lead, and by mask otherwise;
- the device row mask is built from an arange when they lead;
- query lengths are uploaded only in the mixed branch that reads them;
- the device total check joins the opt-in state-contract assertions
  (VLLM_SM70_GDN_STATE_CONTRACT_ASSERT=1).

A GPU test compares the contract with mask indexing for align, all-cache
and block-table state sources under all-spec, spec-then-padding, no-spec and
interleaved rows, and runs the leading cases under CUDA sync debug mode.

V100 TP4, MTP3, E4M3, C1 decode: DCP1 73.10 -> 77.15 tok/s (+5.5%); with
DCP2 packed pages (six GDN groups instead of three) 66.01 -> 70.58 tok/s.
Token outputs and MTP acceptance of the accuracy gate are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Grouped (per-cache-group) CPU offload rows are chosen whenever every
prefix-cacheable group has one global block span, which is the DCP1 hybrid
layout and now also the DCP2 layout with packed target pages. Tiering then
rejected any decode context parallelism, so such a server failed at startup
with "Grouped tiering currently requires single-node TP".

Nothing in the grouped rows depends on DCP: every rank registers and moves
only its own pages, one row slice per worker, and the group spans are
already global block sizes. Keep rejecting pipeline and prefill context
parallelism and multi-node setups.

On V100 TP4/DCP2 the offload regression (eviction under pressure, then
CPU->GPU recovery with external prefix hits and unchanged output) passes on
the grouped rows, matching the DCP1 control.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Under DCP2 each rank holds half of a target QSA layer's main K/V. The
shared CSA+linear pool sizes a block so that one physical page holds one
recurrent (GDN) state, so keeping a whole state-sized page per sharded layer
doubled the global block span (3,200 tokens instead of DCP1's 1,600 on the
E4M3/MTP3 checkpoint). Replicated selector, draft and every request's
recurrent-state blocks paid for that doubled span, short requests fit worse
than on DCP1, and a prefix shorter than 3,200 tokens could never be reused.

A block now spans cache_config.block_size global tokens at every DCP size:
a sharded target layer holds block_size // dcp slots per rank, and the
allocator packs sharded owners whose pages cannot hold a state, in layer
order, as few per physical page as hold one (two at DCP2). A packed page
interleaves its members one kernel block at a time, so each member is a
strided view of the shared tensor and every kernel that honours the block
stride (the cache write and the QSA sparse attention) works unchanged.

- qsa_dcp_block_geometry returns span = block_size; the model asks the
  platform to align the block size to kernel block x dcp so the rank-local
  share stays kernel aligned (an lcm with any user block size).
- The allocator groups owners (_pack_csa_linear_main_kv), places recurrent
  states per physical owner, and emits one KVCacheTensor per physical page
  with packed_members.
- The v2 GPU runner lays out packed members as interleaved strided views;
  the v1 runner rejects packed configs.
- The offload worker registers a packed page once, by its first member,
  covering every member's kernel blocks of that pool block.
- The replicated draft expands its table from the target's rank-local page.

Fewer physical owners per block (7 instead of 13) means six GDN state
groups instead of three; the previous commit removes the per-group host
syncs that otherwise cost DCP2 decode 6.5%.

V100 TP4/DCP2, MTP3, E4M3, graphs, grouped CPU offload:
- block size 1,600 (DCP1 1,600, previous DCP2 span 3,200)
- 32K/C2 capacity 439,296 -> 493,244 tokens (DCP1 396,336)
- 262,144/C4/GMU 0.96 capacity 1,142,744 -> 1,178,337 tokens, 4.50x
  concurrency (DCP1 775,096, 2.96x); allocator projection 1,177,026
- repeated 2K/8K prefix TTFT 834/818 -> 270/256 ms (DCP1 226/235)
- accuracy gate identical to DCP1 (tokens and MTP acceptance); offload
  eviction+recovery, mixed prefill/decode and 5x30K admission regressions
  pass with DCP1-identical outputs

Unit tests cover the geometry, allocator packing and capacity projection,
interleaved views (NHD/HND), offload registration, and a V100 test that
writes packed members through the real cache writer and reads them with
QSA sparse attention bit-identically to unpacked caches (FP16 and E4M3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Under DCP the FlashAttention metadata builder computes every rank's context
lengths for FlashAttention's own DCP attention, a dozen small kernels per
build. The QSA builders inherit it, and QSA's DCP attention localizes its
own selections and never reads those lengths, yet paid for them on every
target and draft build (four per decode step with MTP3).

Add builds_dcp_context_lens to FlashAttentionMetadataBuilder (default
True) and turn it off for Qwen4ExpQSAMetadataBuilder. A test builds the
same DCP2 batch with both and checks that FlashAttention still produces
rank 1's lengths while QSA metadata carries none and matches otherwise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Replace the prototype-stage status with what now serves: the packed target
layout at DCP1's block span, the attention path, the accuracy gate and
regressions, capacity, prefix reuse, decode and TTFT against DCP1 in one
measurement window, and the remaining gaps (XQA-only prefill route, six GDN
state groups, packed pages without pipeline parallelism or the v1 runner).
The prototype checkpoint stays as history.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Hybrid models build GDN attention metadata once per cache group per step.
In steady MTP3 decode one build costs about 0.65 ms of host time on V100
(measured without the profiler), and Qwen4Exp serves its 36 GDN layers in
three cache groups at DCP1 and six at DCP2, so the builds alone cost
2-4 ms of every ~40 ms step while the GPU waits.

The GDN backend already has the fix, used by the SM70 DFlash2 verifier
route: request classification, token indices and offsets are computed
once per step (compute_common_gdn_attn_metadata), and for a pure
speculative decode batch one Triton launch prepares every group's state
indices (prepare_dflash2_gdn_group_metadata); any other batch falls back
to per-group builds. Native MTP verifies a linear draft chain exactly as
DFlash2 does; only the DFlash selector-engine gate kept it out.

Let native MTP use the shared metadata, and for SM70 Qwen4Exp native MTP
(a narrow contract, like the DFlash2 verifier defaults) default
VLLM_SM70_DFLASH2_VERIFY_FASTPATH and VLLM_SM70_DFLASH2_FUSED_GDN_METADATA
to 1; an explicit value still wins and 0 restores per-group builds.

V100 TP4, MTP3, E4M3, graphs, same measurement window: C1 decode
DCP1 72.98 -> 77.92 tok/s (+6.8%), DCP2 71.21 -> 75.76 tok/s. Accuracy
gate (tokens and MTP acceptance) identical to the DCP1 reference on both;
offload eviction/recovery, mixed prefill/decode and five 30K admissions
pass with DCP1-identical outputs, offload bytes and peak memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
…ation

Reuse PR #696 at 9e4236c. Preserve main GDN CPU-index selection and per-group MTP prefix semantics. Add guarded E4M3 defaults, PLE admission and four-rank graph gates; model acceptance remains pending.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
Keep 71c7de1 intact as an integration dependency; E4M3 reduces main KV storage only, not compute reductions.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
…ults

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
Record the installed runtime version and resolve benchmark provenance without shadowing the clean wheel.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
…ntegration-20260927

Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
@Leonccaa

Copy link
Copy Markdown
Contributor

Heads-up for this integration: #696 is now rebased onto current main (1e90d17f2c, head a4210be8aa), 11 commits, mergeable on its own. The previous head 9e4236c2a6 that this PR imported is kept at backup/qwen4exp-qsa-dcp2-stacked-20260926 in the fork, so nothing here breaks.

The DCP2 code is the same as what you imported, with three differences that matter here:

Rebasing this PR onto main after #696 and #664 should leave only your own changes and #701. #664 (E4M3 KV with MTP) merges with #696 without conflicts.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
@Leonccaa

Copy link
Copy Markdown
Contributor

Update for whoever (human or agent) picks up this PR next. The pieces #702 imported are now separate, rebased PRs on current main (1e90d17f2c):

PR head what state
#705 fc402ae7d9 FP32 XQA page4 workspace for E4M3 (fixes #648), 1 file + test ready, CI green
#664 2c1c3f778c calibrated E4M3 KV with MTP (areslp's staging work + draft scales) ready, CI green
#696 a4210be8aa QSA DCP2, incl. its grouped-offload pieces ready, CI green

The three merge onto main without conflicts, in any order; #705 first is simplest because #664 carries the same fix and drops it on rebase. The old heads this PR imported are kept at backup/qwen4exp-qsa-dcp2-stacked-20260926 (#696) and backup/qsa-e4m3-mtp-kv-cache-20260926 (#664) in Leonccaa/1Cat-vLLM.

Suggested next steps for #702

  1. Rebase onto main once [Bugfix][SM70] Allocate the QSA XQA page4 workspace for E4M3 KV #705, [Model][SM70] Calibrated E4M3 KV cache for Qwen4Exp QSA with MTP #664 and [Model][QSA] Target-only DCP2 for Qwen4Exp QSA on TP4 V100 #696 are merged (or onto a local merge of the three heads).
  2. Keep [Model] Qualify calibrated E4M3 and disk PLE with QSA DCP2 #702's own work: [Core] Keep CUDA reductions and PLE memory on safe defaults #701, _apply_sm70_qwen38_kv_defaults and its call site in vllm/config/vllm.py, the disk-PLE admission, and the new benchmarks, tests and docs/design/qwen38_calibrated_dcp_integration.md.
  3. Drop what came from the old [Model][QSA] Target-only DCP2 for Qwen4Exp QSA on TP4 V100 #696 head:
  4. In qwen38_calibrated_dcp_integration.md, "The MTP scale overlay in [Model][QSA] Target-only DCP2 for Qwen4Exp QSA on TP4 V100 #696" is now [Model][SM70] Calibrated E4M3 KV cache for Qwen4Exp QSA with MTP #664.
  5. Check: git grep -nE "uses_native_mtp|_apply_sm70_qwen4exp_mtp_gdn_metadata_defaults|qwen4exp_qsa_dcp2_plan" returns nothing.

Why #705 matters for #702's default: on current main an E4M3 server on the QSA DCP1 path stops during startup (XQA decode tmp_out must be fp32 for E4M3 KV and fp16 otherwise). The E4M3-without-MTP default here needs #705 or #664.

What we measured on main 1e90d17f2c (4×V100, TP4, CUDA graphs, prefix caching, 32 GiB grouped CPU offload, fresh servers). The gate is six MTP prompts with accepted/drafted counts, five code repeats, and a 30,030-token prompt with prefix-hit repeat and changed suffix.

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.

3 participants