Repository navigation
[DSV4][ROCm] Add contract-bound C4 KV compressor (task 19) #618
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
inaniloquentee
wants to merge
2
commits into
dsv4-rocm
Choose a base branch
from
feat/dsv4-kv-c4-bi
base: dsv4-rocm
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| """MI300X benchmark for the fixed aiter C4 overlap forward boundary.""" | ||
|
|
||
| import argparse | ||
| import json | ||
| from pathlib import Path | ||
| import sys | ||
| import time | ||
|
|
||
| import torch | ||
|
|
||
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) | ||
|
|
||
| from rl_engine.backends.rocm.attention.kv_compressor_c4 import AiterC4Backend | ||
| from rl_engine.contracts.operators.kv_compressor_c4 import C4Contract | ||
| from rl_engine.models.deepseek_v4.kv_compressor_c4 import C4Compressor | ||
| from tests.models.deepseek_v4.c4_helpers import fixture | ||
|
|
||
|
|
||
| def timed(fn, repeats): | ||
| for _ in range(5): | ||
| fn() | ||
| torch.cuda.synchronize() | ||
| start = time.perf_counter() | ||
| for _ in range(repeats): | ||
| fn() | ||
| torch.cuda.synchronize() | ||
| return (time.perf_counter() - start) * 1e6 / repeats | ||
|
|
||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser() | ||
| parser.add_argument("--repeats", type=int, default=30) | ||
| parser.add_argument("--output", type=Path, required=True) | ||
| args = parser.parse_args() | ||
| backend = AiterC4Backend() | ||
| operator = C4Compressor(C4Contract(), backend=backend) | ||
| cases = [] | ||
| for groups in (1, 8, 64, 128): | ||
| values, ids, ape, norm, cos, sin = fixture(groups, device="cuda") | ||
| bound = operator.bind(values, ids, version="v1") | ||
|
|
||
| def forward(bound=bound, ape=ape, norm=norm, cos=cos, sin=sin): | ||
| operator(bound, ape, norm, cos, sin, version="v1") | ||
|
|
||
| cases.append( | ||
| { | ||
| "groups": groups, | ||
| "tokens": groups * 4, | ||
| "input_bytes": values.numel() * values.element_size(), | ||
| "output_bytes": groups * 512 * 2, | ||
| "bind_us": timed( | ||
| lambda values=values, ids=ids: operator.bind(values, ids, version="v1"), | ||
| args.repeats, | ||
| ), | ||
| "forward_us": timed(forward, args.repeats), | ||
| "provider_forward_us": timed( | ||
| lambda bound=bound, ape=ape, norm=norm, cos=cos, sin=sin: backend.forward( | ||
| bound, ape, norm, cos, sin, operator.contract | ||
| ), | ||
| args.repeats, | ||
| ), | ||
| } | ||
| ) | ||
| result = { | ||
| "gpu": torch.cuda.get_device_name(), | ||
| "arch": torch.cuda.get_device_properties(0).gcnArchName, | ||
| "torch": torch.__version__, | ||
| "hip": torch.version.hip, | ||
| "triton": __import__("triton").__version__, | ||
| "backend": backend.provenance, | ||
| "contract": operator.contract.fingerprint, | ||
| "implementation": operator.implementation_fingerprint, | ||
| "metric": ( | ||
| "synchronized average over warmup+repeated loops; Python validation and " | ||
| "paged-cache gather included" | ||
| ), | ||
| "scope": ( | ||
| "complete C4 forward; aiter overlap pool and norm/RoPE; no live cache " | ||
| "commit, no full model" | ||
| ), | ||
| "cases": cases, | ||
| } | ||
| args.output.write_text(json.dumps(result, indent=2)) | ||
| print(json.dumps(result, indent=2)) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| # DSV4 item 19: C4 KV compressor | ||
|
|
||
| This item provides the main-KV C4 compressor with the fixed eight-token overlap | ||
| window. It is separate from: | ||
|
|
||
| - item 18 C128 main-KV compression; | ||
| - item 20 the independent 128-dimensional Indexer C4 compressor; | ||
| - item 21 state/cache lifecycle and exactly-once commit; | ||
| - item 27 prefill/decode/recompute/Graph runtime integration. | ||
|
|
||
| ## Contract | ||
|
|
||
| ```text | ||
| input: BF16 [tokens, 2048] | ||
| [content1024, score1024] where each 1024-wide stream has two 512 halves | ||
| int64 logical token IDs [request_id, global_token_position] | ||
| output: BF16 [complete_groups, 512] | ||
| logical IDs [request_id, global_group_id] | ||
| ``` | ||
|
|
||
| The compressor consumes four current tokens per output row. For group `g`, the | ||
| softmax window is: | ||
|
|
||
| ```text | ||
| previous group content/gate first half (g-1), then | ||
| current group content/gate second half (g) | ||
| ``` | ||
|
|
||
| The first group has four masked predecessor rows. Incomplete groups are never | ||
| padded into a submitted output. Global position is `group_id * 4`; local rank | ||
| order is not substituted for it. The optional `emit_group_ids` binding argument | ||
| allows a microbatch to include one completed predecessor group as context while | ||
| emitting only its requested groups. | ||
|
|
||
| CP callers must provide complete current groups and their overlap predecessor | ||
| before binding. The rank owning the current group’s fourth token emits that row. | ||
| No collective is performed by this item. | ||
|
|
||
| ## Reuse and paged output | ||
|
|
||
| The ROCm backend directly reuses aiter’s | ||
| `flydsl_fused_compress_attn` with `overlap=True`, `ratio=4`, `head_dim=512`, | ||
| BF16 output, and the fixed one-wave (`k_split_num_waves=1`) arithmetic path. | ||
| The private output uses the provider’s real compressed-cache layout: | ||
| `k_per_block=64`, a per-request block table, and cache rows selected by | ||
| `group_id % 64`. This is important for correctness; treating every row as a | ||
| one-row block is not an accepted shortcut. | ||
|
|
||
| The overlap input is materialized as eight direct input rows per plan row. This | ||
| reuses aiter’s optimized online-softmax/norm/RoPE kernel while leaving live | ||
| request state mutation and cache commit to item 21. The first group’s four | ||
| predecessor scores use a finite BF16 floor below the admitted score range, | ||
| avoiding the input-phase `-inf - (-inf)` case; the independent oracle retains | ||
| the exact masked semantics. | ||
|
|
||
| Backward reuses the pinned mLite/Megatron `compressed_compact_graph` ratio-4 | ||
| functional graph. One output’s VJP is evaluated against its previous/current | ||
| context. Overlapping row contributions are applied in a fixed Python logical | ||
| order, not with unordered floating-point atomics. APE and RMS rows are | ||
| registered with RLK’s existing `CanonicalBackwardSession`. | ||
|
|
||
| Provider source hashes are checked before admission; missing or different | ||
| aiter/mLite sources fail closed. No PyTorch numerical fallback is exposed. | ||
|
|
||
| ## Verification | ||
|
|
||
| The MI300X tests cover: | ||
|
|
||
| - complete and partial groups, first-group masking, overlap previous/current | ||
| halves, global positions, request permutation and padding; | ||
| - CP final-token ownership, context predecessor admission and block-table | ||
| mapping; | ||
| - softmax/content scale extremes, fixed overlap plan, `k_per_block=64`, | ||
| output/cache isolation, streams and fresh-process/JIT fingerprints; | ||
| - independent FP64 forward arithmetic; | ||
| - full/microbatch/reversed-order backward, overlap gradient accumulation and | ||
| independent autograd/finite-difference checks; | ||
| - malformed ABI/metadata, budget, mutation, version and Graph rejection. | ||
|
|
||
| This item does not claim RCCL/multi-GPU CP, live cache commit, Graph replay, | ||
| prefill/decode framework integration or full-model acceptance. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| # Item 19 C4 KV compressor — MI300X validation | ||
|
|
||
| Branch: `feat/dsv4-kv-c4-bi` | ||
| Target: `dsv4-rocm` | ||
| Hardware: AMD Instinct MI300X VF (`gfx942`) | ||
| Environment: ROCm/HIP 7.14.60850, PyTorch 2.12.0+rocm7.14.0a20260608, | ||
| Triton 3.7.0, FlyDSL 0.3.4.1, pinned aiter commit | ||
| `326cffa38c9eda16bef19375ba55334e4d3acd59`, pinned mLite commit | ||
| `327a238239448de6771beea185dc6902a8de3eaa`. | ||
|
|
||
| ## Test evidence | ||
|
|
||
| - CPU-safe C4 tests: **53 passed**. | ||
| - Real MI300X C4 tests: **26 passed**, 0 skipped, 1 existing warning, 19.49s. | ||
| - Full targeted contract + DeepSeek-V4 suite: **221 passed**, 0 skipped, 1 | ||
| existing warning, 20.33s without coverage and 26.54s with coverage. | ||
| - Scoped Python wrapper coverage: **336/371 statements (90%)** and **80/86 | ||
| branches**. Coverage does not measure FlyDSL/native GPU instructions; the | ||
| unexecuted wrapper lines are provider-constructor and direct native VJP | ||
| plumbing branches exercised by the real GPU tests. | ||
| - Ruff format/lint passed for all ten Python files. | ||
| - Real GPU coverage includes aiter overlap compression, true `k_per_block=64` | ||
| paged output mapping, mLite ratio-4 functional VJP, fixed-order overlap | ||
| gradients, streams, fresh processes/JIT isolation and CP ownership. | ||
| - The forward oracle is independent FP64 softmax/pool/RMS/rotation arithmetic. | ||
| - The backward checks compare fixed-order microbatch/reverse-order gradients to | ||
| independent autograd and finite-difference results. | ||
|
|
||
| This item does not claim RCCL/multi-GPU CP, Graph replay, live cache commit, | ||
| prefill/decode runtime or full-model acceptance. | ||
|
|
||
| The benchmark records bind, provider forward and full operator timing with | ||
| Python validation, paged-cache gather and synchronization included. It is not a | ||
| kernel-only or throughput claim. | ||
|
|
||
| ## Synchronized MI300X benchmark (30 repetitions) | ||
|
|
||
| | Complete groups | Tokens | Bind (us) | Provider (us) | Full forward (us) | | ||
| |---:|---:|---:|---:|---:| | ||
| | 1 | 4 | 429.67 | 95.44 | 440.68 | | ||
| | 8 | 32 | 944.07 | 221.16 | 781.28 | | ||
| | 64 | 256 | 2,805.11 | 533.89 | 1,096.21 | | ||
| | 128 | 512 | 5,045.03 | 380.60 | 912.69 | | ||
|
|
||
| These are synchronized average wall times with warmup, Python validation, | ||
| paged-cache gather and dispatch included. They are not kernel-only latency or | ||
| a speedup claim. The raw benchmark records the provider source fingerprints. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # C4 follow-up: new MI300X environment, 2026-10-29 | ||
|
|
||
| This is a corrective follow-up to task 19 / PR #618, not a new task PR. | ||
|
|
||
| ## Corrections | ||
|
|
||
| - The first group's four missing predecessors now use the existing aiter | ||
| state-phase negative-position mask. The old finite `-2**30` gate floor was | ||
| not a valid universal mask over the admitted gate domain. No handwritten | ||
| numerical kernel or approximate masking workaround is used. | ||
| - Validate the predecessor context as well as the emitted current-group rows. | ||
| An `emit_group_ids` filter cannot hide non-finite predecessor data. | ||
| - The independent oracle now preserves FP64 throughout softmax, pooling, | ||
| normalization and rotation. The previous helper converted its inputs to | ||
| FP32 internally, so the earlier report's description of that comparison as | ||
| fully FP64 was incorrect. New regression tests verify FP64 perturbations | ||
| survive and gradients remain FP64. | ||
| - Backward passes host-known unique context indices instead of iterating GPU | ||
| index tensors and synchronizing each index. | ||
|
|
||
| ## Dependency profile | ||
|
|
||
| The new image's existing aiter 0.1.16.post3 / FlyDSL 0.2.0 provider is admitted | ||
| by its exact source hash, alongside the previously pinned provider ABI. | ||
| The older ABI has a fixed single-wave path without a batch-size dispatch. | ||
| No numerical fallback is selected on a library error. | ||
|
|
||
| - PyTorch: `2.11.0+gitd0c8b1f` | ||
| - HIP: `7.2.53211` | ||
| - Triton: `3.6.0` | ||
| - GPU architecture: `gfx942` | ||
| - aiter C4 source SHA256: | ||
| `0c2019ca3a4a0d6dc7d5fdd20ebcce658ff5f504fdc880dffd61c9c398029451` | ||
| - mLite functional backward source SHA256: | ||
| `6a1e78ca89b83f56391b5e5ac409e765338f1e3a625251d0d4c877c1e80b4a68` | ||
|
|
||
| ## Tests | ||
|
|
||
| - C4 CPU/GPU tests: **85 passed**, one warning, 23.66 seconds. | ||
| - Full targeted contract + DeepSeek-V4 suite: **227 passed**, zero skips, | ||
| one warning, 34.38 seconds with coverage. | ||
| - Includes four extreme first-group score biases, hidden non-finite context, | ||
| real FP64 oracle checks, real aiter launches, mLite VJP, streams and fresh | ||
| subprocess runs. | ||
| - Changed-file Ruff lint/format checks pass; the 30-repetition benchmark was | ||
| re-run on the new environment. | ||
| - Python wrapper coverage remains approximately 90%. Native instructions and | ||
| autograd-internal tracing gaps are not evidence of native coverage; earlier | ||
| statements describing all uncovered lines as constructor failures were too | ||
| broad. | ||
|
|
||
| Logs, JUnit XML, coverage and benchmark JSON are under the member's mounted | ||
| `/outputs/c4-new-host-20261029/`, not the shared Docker writable layer. | ||
| The task-19 limits remain: no RCCL/multi-GPU CP, Graph replay, live cache | ||
| commit, full-model or end-to-end framework acceptance. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This times host wall clock around an async device loop only. Peers in this repo report GPU-event and wall time together (see
benchmarks/backends/rocm/benchmark_rocm_singleton_lse.py::_elapsed_ms). Without the event pair,forward_us/provider_forward_usare dominated by Python-side work in the timed region:torch.empty+ twozeros_likeinAiterC4Backend.forward, the(blocks, 64, 512)BF16 cache allocation, theclone()ofcontext_values, the Python loop overlogical_keys, and (forbind_us) a blockingtoken_ids.cpu().tolist(). Recording kernel time alongside wall time makes it explicit which part is host/launch overhead, so the JSON is not read as kernel latency.Also, this
torch.cuda.synchronize()and thedevice="cuda"fixtures inmain()are unconditional, so the script cannot run (or clearly skip) on a non-CUDA/ROCm host; the repo's guarded pattern isif device.type == "cuda"(benchmark_rocm_logp.py::_device_synchronize).Suggestion: