Skip to content

perf(dsv4): speed up decode indexer score kernel at 8k#695

Merged
zhangqi-chen merged 1 commit into
hw-native-sys:mainfrom
zhangqi-chen:perf-indexer
Jul 6, 2026
Merged

perf(dsv4): speed up decode indexer score kernel at 8k#695
zhangqi-chen merged 1 commit into
hw-native-sys:mainfrom
zhangqi-chen:perf-indexer

Conversation

@zhangqi-chen

Copy link
Copy Markdown
Collaborator

Summary

  • At start_pos=8192 the DeepSeek-V4 decode indexer score SPMD scales with compressed KV length and dominates the kernel (~288us). Restructure it into three fanned stages plus intra-lane cuts, down to ~42us on a2a3.
  • Split the fused mat+vec score path into three GM-handoff stages so the vector-bound work is not pinned by the cube pairing: score_kv_quant (vec) → score_mat (cube, MAT_TILE=512) → score_reduce (vec, pl.pipeline(stage=2), REDUCE_TILE=128).
  • Fan score_kv_quant and score_reduce across NSPLIT=4 extra lanes (T * NSPLIT = 32), striding the cache tiles per lane.
  • CACHE_TILE 32 → 64; compute the INT8 amax from a single abs tile (drop the chunked re-cast loop). Keep the abs-based max(|x|): max(row_max, -row_min) is wrong on real signed KV (the standalone fixture is all-positive so it never caught it).
  • Fold the per-position dequant scale past the ReLU and head-sum; inline the topk helper back into indexer.
  • Validated on a2a3: standalone decode_indexer.py and decode_attention_csa.py (x_out + kv_cache) both PASS. The extra lanes contend for vector cores, so the end-to-end win under decode_fwd should be re-checked.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

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

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4f584654-f0ff-43c6-b5b1-35f46791032b

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
📝 Walkthrough

Walkthrough

The scoring stage in the decode indexer is refactored into a three-phase pipeline: INT8 KV quantization with dequant scales, INT8 matmul producing INT32 accumulators, and a reduce/dequant stage writing score_flat. New tiling constants are added, and TopK selection is inlined into indexer, removing indexer_topk_core.

Changes

Decode indexer scoring refactor

Layer / File(s) Summary
Tiling constants for staged compute
models/deepseek/v4/decode_indexer.py
CACHE_TILE raised to 64 with a BLOCK_SIZE divisibility check; new MAT_TILE, REDUCE_TILE, QUANT_NSPLIT, REDUCE_NSPLIT constants added.
KV quantization + INT8 matmul phases
models/deepseek/v4/decode_indexer.py
KV is quantized into GM scratch buffers (kv_q_i8_gm, kv_dq_gm), then a separate matmul phase computes INT32 score accumulators (score_acc_gm) via INT8 KV × quantized Q.
Score reduce/dequant stage
models/deepseek/v4/decode_indexer.py
score_acc_gm is dequantized, ReLU-clamped, weighted, reduced with kv_dq_gm, invalid positions padded with -inf, and result written into score_flat.
Inlined TopK selection
models/deepseek/v4/decode_indexer.py
indexer_topk_core is removed; TopK logic (sort32, mrgsort, merge, offset addition) is inlined directly in indexer using score_flat.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: enhancement

Poem

A hop, a skip, three stages deep,
Quantize, matmul, reduce we leap.
No more loops for TopK's quest,
Inlined now, it works its best.
🐇 score_flat gleams, so crisp and bright!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: a performance improvement to the DeepSeek-V4 decode indexer score kernel at 8k.
Description check ✅ Passed The description matches the changeset, describing the score-kernel refactor, tiling/splitting changes, and topk inlining.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
models/deepseek/v4/decode_indexer.py (1)

229-341: 🚀 Performance & Scalability | 🔵 Trivial

Operational: recheck vector-core contention end-to-end before merge.

The QUANT_NSPLIT/REDUCE_NSPLIT fanout raises effective parallelism to T * NSPLIT = 32 vector lanes contending with other decode stages. As the PR notes, decode_fwd end-to-end perf/accuracy wasn't revalidated after this change. Recommend capturing decode-path metrics (vector-core occupancy, stage latency at start_pos=8192) under the full pipeline, not just the standalone kernel, before landing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@models/deepseek/v4/decode_indexer.py` around lines 229 - 341, The fanout
change in the score path increases vector-lane contention, so revalidate the
full decode pipeline before merging. Re-run end-to-end `decode_fwd`/decode-path
measurements with this `QUANT_NSPLIT` and `REDUCE_NSPLIT` setup, focusing on
vector-core occupancy and stage latency at `start_pos=8192`. Confirm the impact
under the complete pipeline, not just the standalone kernel, and only keep the
change if the metrics and accuracy remain acceptable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@models/deepseek/v4/decode_indexer.py`:
- Around line 229-341: The fanout change in the score path increases vector-lane
contention, so revalidate the full decode pipeline before merging. Re-run
end-to-end `decode_fwd`/decode-path measurements with this `QUANT_NSPLIT` and
`REDUCE_NSPLIT` setup, focusing on vector-core occupancy and stage latency at
`start_pos=8192`. Confirm the impact under the complete pipeline, not just the
standalone kernel, and only keep the change if the metrics and accuracy remain
acceptable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a9e828d7-54e5-4dc3-9ba9-3e39aa7213f9

📥 Commits

Reviewing files that changed from the base of the PR and between b681b94 and 90313bd.

📒 Files selected for processing (1)
  • models/deepseek/v4/decode_indexer.py

With decode defaulting to start_pos=8192, the indexer score SPMD scales
with compressed KV length and dominated the kernel (~288us). Restructure
it into three fanned stages plus intra-lane cuts, to ~42us on a2a3.

- CACHE_TILE 32 -> 64; INT8 amax via a single abs tile (drop the chunked
  re-cast loop). Keep the abs-based max(|x|): max(row_max, -row_min) is
  WRONG on real signed KV (decode_attention_csa x_out fails ~27%); the
  standalone indexer fixture is all-positive so it never caught it.
- fold the per-position dequant scale past the ReLU and head-sum, and
  apply the same reorder in golden_indexer so the fp32 op order matches
- inline the topk helper back into indexer
- split the fused mat+vec score path into three GM-handoff stages so the
  vector-bound work is not pinned by the cube pairing:
  score_kv_quant (vec) -> score_mat (cube, MAT_TILE=512) ->
  score_reduce (vec, pl.pipeline(stage=2), REDUCE_TILE=128)
- fan score_kv_quant and score_reduce across NSPLIT=4 extra lanes
  (T * NSPLIT = 32), striding the cache tiles per lane

Validated: standalone decode_indexer.py and decode_attention_csa.py
(x_out + kv_cache) both PASS on a2a3. The extra lanes contend for vector
cores, so re-check the end-to-end win under decode_fwd.
@zhangqi-chen zhangqi-chen merged commit 628dcf6 into hw-native-sys:main Jul 6, 2026
5 of 7 checks passed
@zhangqi-chen zhangqi-chen deleted the perf-indexer branch July 6, 2026 08:06
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