Skip to content

feat(dsv4): canonical BI sparse attention and sink VJPs (item 25) - #629

Open
inaniloquentee wants to merge 24 commits into
dsv4-rocmfrom
feat/dsv4-joint-sparse-backward-bi
Open

inaniloquentee wants to merge 24 commits into
dsv4-rocmfrom
feat/dsv4-joint-sparse-backward-bi

Conversation

@inaniloquentee

Copy link
Copy Markdown
Collaborator

Roadmap item 25 ??sparse attention and sink backward

Target: dsv4-rocm. Task-only commit: ce1c45d322191c8ff03e7dfe2a568524729bcf63.
Prerequisite: item 24 / #628 (159b0592be933b65aac76ff8b9fe411fa3f27456), itself on current main 8588158650aff58c9c1f527674de94bad06c3d10 (rechecked). Review task-only changes against 159b0592; the target diff includes its unmerged forward prerequisite.

Native reuse, not rewritten kernels

  • aiter MHA delta preprocessing plus pinned sparse dQ/P/dP, K=V contribution and CSR-gather kernels.
  • Disable ONLY the upstream sink atomic branch; retain the real sink-inclusive LSE for dQ/P/dP.
  • Fixed 32-query Torch sink tiles followed by a fixed full-set merge.
  • Reuse RLK CanonicalBackwardSession: collect per-query contributions, canonically sort, and reduce dKV/d_sink once. Never sum independently rounded microbatch parameter gradients.
  • Explicit BF16 q/KV master gradients, FP32 sink gradient, no LSE or higher-order derivative claim.
  • Fixed-record native backward Graph, owned output snapshots, source/CSR mutation and workspace/event-owner barriers; opt-in Megatron bridge.

MI300X evidence

  • 306 passed, zero skipped, twice; 70 new tests (26 CPU, 44 real ROCm), plus 236 prerequisite/contract regressions.
  • FP64 gradient oracle, repeated shared KV, exact microbatch/permutation/backward-arrival invariance, CSR64/sink32 boundary crossing, truly empty KV, padded heads, disabled sink, H128/D512/640 selected entries.
  • Actual backward Graph replay, changing dO, streams, selective gradients and mixed backward-profile rejection.
  • Runtime coverage: 306/306 statements and 42/42 branches. Coverage-only execution disables autograd worker threading so Python callbacks are traced; the independent repeat and benchmarks retain default threading. Native GPU instructions are not counted, and 100% Python coverage is not a claim of universal correctness.
  • Ruff passes. Source/provider hashes, checked API overhead and native/Graph timings are committed under reports/joint-backward-mi300x-20261029.{md,json}.

Explicit boundaries

The eager canonical autograd collector is not a captured whole training step. Fixed-record Graph does not claim dynamic CSR capture or framework checkpoint orchestration. CP/TP transport, quantized cache-byte gradients, higher-order gradients, full engines/models and cross-parallel-degree equality remain separate roadmap work. Native training VJPs use the upstream BF16 P/dP approximation; discrete rounding derivatives are not claimed. No baseline-engine speedup is inferred from the timings.

fangfangssj and others added 24 commits October 24, 2026 08:44
Signed-off-by: fangfangssj <1135470306@qq.com>
Signed-off-by: Ziyi <ziyizhao0723@gmail.com>
ws1-gtest-gpu and ws1-chain-gpu fail at "Configure runpodctl" before any
test runs. They install releases/latest (v2.14.0 since 2026-09-10), whose
deprecated `config --apiKey` aborts with
`Config File ".runpod.yaml" Not Found` when it is the first runpodctl
call on a fresh HOME. It only succeeds once an earlier call has created
~/.runpod/config.toml, which is why gpu-ci, whose install step happens to
run `runpodctl version` first, got past the same command.

Drop the config step in all three workflows. runpodctl v2 reads
RUNPOD_API_KEY from the environment, and every step that runs
ci/run_gpu_ci.sh already exports it, including the cleanup trap that
calls `pod remove`. Verified for v2.9.0 inside `unshare -rn`, from a
fresh HOME after `runpodctl version`: without the variable `pod list`
fails locally with no_credentials, with RUNPOD_API_KEY=dummy it gets
past the credential check to a network_error, and config.toml keeps
`apikey = ''` throughout. v2.14.0 behaves the same.

Pin the download to v2.9.0, the version gpu-ci run 31455127310 exercised
end to end (pod create with sold-out fallback, pod id parsing, pod get
polling, SSH, cleanup), and verify it with sha256sum -c against the
runpodctl-linux-amd64 line of checksums_2.9.0_sha256.txt
(06e6f54957db79d5cd9f1909a7f1d365076826751ba2f5df65d75dde43a64148),
which matches GitHub's asset digest.

Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
fix(ci): pin runpodctl v2.9.0 and drop the deprecated config step (RUNPOD_API_KEY auth)
Signed-off-by: maxiaosong1124 <maxiaosong7890@outlook.com>
Signed-off-by: maxiaosong1124 <maxiaosong7890@outlook.com>
Signed-off-by: maxiaosong1124 <maxiaosong7890@outlook.com>
Signed-off-by: maxiaosong1124 <maxiaosong7890@outlook.com>
ci: remove obsolete RunPod WS1-chain-GPU workflow
Signed-off-by: maxiaosong1124 <maxiaosong7890@outlook.com>
Signed-off-by: maxiaosong1124 <maxiaosong7890@outlook.com>
Signed-off-by: maxiaosong1124 <maxiaosong7890@outlook.com>
Signed-off-by: maxiaosong1124 <maxiaosong7890@outlook.com>
ci: add RL-Kernel-specific automatic OCR review
Signed-off-by: maxiaosong1124 <maxiaosong7890@outlook.com>
Signed-off-by: maxiaosong1124 <maxiaosong7890@outlook.com>
Signed-off-by: lamentropetion <3051000145@qq.com>
docs(rocm): make the vLLM companion base reproducible from upstream
[Ascend] Fix deterministic attention backward compilation
@coderabbitai

coderabbitai Bot commented Oct 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 26b1ec6b-75df-451f-907a-78952952da4d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

🔍 OpenCodeReview found 9 issue(s) in this PR.

  • ✅ Successfully posted inline: 5 comment(s)
  • 📋 Routed to summary by policy: 4 comment(s)

⚠️ 4 warning(s) occurred during review.


security · medium

📄 .github/workflows/ocr-review.yml (L18-L20)

⚠️ GitHub could not post this as an inline comment: Routed to summary (severity medium · category security)

pull_request_target workflows run with access to secrets.OCR_LLM_AUTH_TOKEN and a write-scoped github.token, while the PR head is controlled by the (potentially forked) contributor. Broad branch patterns here (test-*, dsv4-*, p6-*) mean any PR targeting a matching branch — including branches that may not be protected or reviewed — triggers a privileged run. Since these secrets belong to the provider and the token can comment on PRs, restrict the trigger to protected branches only (or gate the job on github.event.pull_request.head.repo.full_name == github.repository) so fork PRs cannot reach this path.


maintainability · low

📄 benchmarks/benchmark_joint_backward.py (L61-L61)

⚠️ GitHub could not post this as an inline comment: Routed to summary (severity low · category maintainability)

The forward record here is executed eagerly (use_graph defaults to False). For the largest shape selected_capacity = 512 + 128 = 640 and m=1, workspace_bytes already exceeds 1 << 30 for max_queries=1? No — but note JointAttentionContract defaults max_queries/max_kv to the values passed here, so no workspace issue. The real problem: step() re-enters adapter(...) which calls plan.forward_record again after the plan's buffers were frozen by backward = JointBackwardPlan(plan, saved). JointBackwardPlan.capture()/backward() assert record.validate_saved() for that specific record, but plan.forward_record zeroes and overwrites plan._q/_kv/_indices, which are not the record snapshots (they are clones), so this is safe. However adapter ignores saved/backward entirely and no assertion ties the timed canonical path to the same captured backend path; record backward.backward_implementation provenance only.


maintainability · low

📄 benchmarks/benchmark_joint_backward.py (L50-L50)

⚠️ GitHub could not post this as an inline comment: Routed to summary (severity low · category maintainability)

The third comment above was posted in error: the canonical step() path is consistent with JointForwardRecord snapshots (each forward_record call clones its own outputs), so the repeated forward_record inside step() is not a defect. Please disregard it — no change is required for that point.


bug · medium

📄 .github/workflows/ocr-review.yml (L97-L99)

⚠️ GitHub could not post this as an inline comment: Routed to summary (severity medium · category bug)

rule: .opencodereview/rule.json is a relative path resolved against the action's checkout. Per the comments here (and docs/contributing/ocr-review.md), the pinned action checks out the selected branch rather than necessarily the default branch; there is no explicit ref input, so any unpublished change to the rule file in the checked-out revision silently changes review behavior. More importantly, .opencodereview/rule.json is added by this same PR, so on the first run after merge of the integration PR (and for any PR whose base lacks the file) the action receives a non-existent rule path. Confirm the action's behavior when the rule file is missing (fail vs. fall back to built-in prompts) and, if it fails or silently falls back, add a fallback/guard step that verifies the file exists in the trusted checkout before invoking the action.


⚠️ Warnings:

  • benchmarks/benchmark_joint_attention.py (token_budget_reached): skipped round 2 of group "benchmarks/benchmark_joint_attention.py,benchmarks/benchmark_joint_backward.py": used 513004 tokens exceeds budget 500000
  • rl_engine/backends/rocm/attention/joint_attention.py (token_budget_reached): stopped dispatch: used 513004 tokens + group estimate 69496 = projected 582500 exceeds budget 500000
  • .github/CODEOWNERS,.github/workflows/gpu-ci.yml,.github/workflows/ocr-review.yml,.github/workflows/ws1-gtest-gpu.yml,.opencodereview/rule.json (review_limit_reached): reached the aggregate token budget before finishing; the grace round did not complete the task
  • docs/.nav.yml,reports/joint-attention-mi300x-20261029.json,reports/joint-backward-mi300x-20261029.json (review_limit_reached): reached the aggregate token budget before finishing; the grace round completed the task

@@ -43,15 +43,20 @@ jobs:
ref: ${{ github.event.pull_request.base.sha }}

- name: Install runpodctl

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · high
The separate Configure runpodctl step (runpodctl config --apiKey "${{ secrets.RUNPOD_API_KEY }}") was removed, and the API key is now only exported as an env var in the later "Run GPU tests on RunPod" step. ci/scripts/run_gpu_ci.sh invokes runpodctl pod create/get/remove without any --api-key flag or runpodctl config call, so unless the pinned CLI happens to read RUNPOD_API_KEY from the environment, every runpodctl call will run unauthenticated and the job (including pod cleanup) will fail. Please either restore an explicit configure step (e.g. runpodctl config --apiKey "$RUNPOD_API_KEY") or confirm/encode the env-var mechanism the CLI uses.

@@ -89,13 +89,19 @@ jobs:
persist-credentials: false

- name: Install runpodctl

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · high
Same as gpu-ci.yml: the Configure runpodctl step that performed runpodctl config --apiKey "${{ secrets.RUNPOD_API_KEY }}" was dropped. ci/scripts/run_gpu_ci.sh calls runpodctl pod create/get/remove without passing --api-key, so without an explicit configure step the CLI is unauthenticated and the GPU job plus its cleanup trap will fail. Restore the configure step or confirm the pinned CLI reads the API key from the environment.

Comment thread .opencodereview/rule.json
Comment on lines +2 to +6
"include": [
"**/*.{cu,cuh,hip}",
"tests/**",
"rl_engine/validation/**"
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · high
The rules list contains focused rules for {csrc,rl_engine/backends,rl_engine/kernels}/**, rl_engine/distributed/**, rl_engine/runtime/**, rl_engine/integrations/**, rl_engine/{ops,contracts,reference,models,layers}/** and {.github,ci,build_tools}/**, but include only lists **/*.{cu,cuh,hip}, tests/** and rl_engine/validation/**. If include acts as a selection filter (the docs describe it as overriding OCR's default extension/path filters so that .cu/.cuh/.hip are not dropped), then rl_engine/**/*.py files — e.g. rl_engine/integrations/orchestrators/..., rl_engine/models/deepseek_v4/*.py, rl_engine/backends/rocm/** — are never submitted for review and every Python-specific rule above becomes dead configuration. This PR is exactly such a case (DeepSeek-V4 model/backend/integration Python files are not matched by any include entry). Verify with ocr rules check rl_engine/models/deepseek_v4/joint_attention.py; if it is not selected, add rl_engine/**/*.py (and ci/**) to include.

Comment on lines +43 to +44
kvkeys = [[0, 0, i * 4 + 3] for i in range(topk)]
kvkeys.extend([0, 1, p] for p in range(start - window + 1, start + m))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · high
The kvkeys layout only ever uses request id 0, while qkeys spans m requests (e.g. m=7, m=65). JointMetadata.bind requires every active query's Top-K rows to satisfy kh[i][0] == request, so for m > 1 any selected index whose key has request == 0 is rejected with "cross-request, future or noncompressed Top-K row" — and since the selected tensor is the same arange(topk) for all m rows, only rows for request 0 can ever be valid. The script therefore cannot run for 3 of its 4 shapes (and the remaining benches an m=1 degenerate case). Emit per-request keys like benchmark_joint_attention.py does, plus matching per-request selected indices:

Suggestion:

Suggested change
kvkeys = [[0, 0, i * 4 + 3] for i in range(topk)]
kvkeys.extend([0, 1, p] for p in range(start - window + 1, start + m))
kvkeys, selected = [], []
for request in range(m):
offset = len(kvkeys)
kvkeys.extend([request, 0, i * 4 + 3] for i in range(topk))
kvkeys.extend([request, 1, p] for p in range(start - window + 1, start + m))
selected.append(list(range(offset, offset + topk)))

c,
torch.tensor(qkeys, device="cuda"),
torch.tensor(kvkeys, device="cuda"),
torch.arange(topk, dtype=torch.int32, device="cuda")[None].repeat(m, 1),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · high
This still uses the old single-request selected tensor (arange(topk) repeated for all m rows) and the old (start - window + 1, start + m) window. With the fix above, the window must be built per request (it currently emits m window rows per request while bind only expects the window_size rows ending at that query's position), and the indices must be the corresponding per-request selected lists materialized as an int32 tensor of shape (m, topk).

Suggestion:

Suggested change
torch.arange(topk, dtype=torch.int32, device="cuda")[None].repeat(m, 1),
torch.tensor(selected, dtype=torch.int32, device="cuda"),

@github-actions

Copy link
Copy Markdown

@inaniloquentee The automated OCR review has completed.

Please review and address all automated review bot findings, then push your updates. Maintainers will begin manual review after the bot findings have been addressed.

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

deepseek-P2 DSv4 platform: rocm Specific tasks specific to AMD graphics cards (such as CK, bpreshuffle/FA)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants