Skip to content

deepseek/v4: align cache layout constants#674

Merged
zhangqi-chen merged 1 commit into
hw-native-sys:mainfrom
high-cloud:fix/dsv4-cache-layout-constants
Jul 2, 2026
Merged

deepseek/v4: align cache layout constants#674
zhangqi-chen merged 1 commit into
hw-native-sys:mainfrom
high-cloud:fix/dsv4-cache-layout-constants

Conversation

@high-cloud

Copy link
Copy Markdown
Contributor

Summary

  • Define shared DeepSeek V4 paged-cache block counts in config.py and reuse them across decode and prefill cache users.
  • Size prefill compressed KV and index KV pools from the shared decode-batch physical layout while keeping table widths explicit.
  • Return hidden states from L3 prefill/decode forward kernels for the serving-side lm_head path and raise the MoE recv-buffer floor for the current distributed layout.

Related Issues

N/A

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces centralized static paged-cache pool constants in config.py and refactors various decode and prefill attention/compressor modules to use these unified configuration variables instead of hardcoded values. Additionally, it removes the lm_head_tp call and its associated buffer allocations from both decode_fwd.py and prefill_fwd.py, returning hidden_out directly instead of logits. Feedback on these changes suggests updating an outdated comment in config.py regarding the receiver buffer floor, removing the now-unused lm_head_weight parameter and imports from the forward pass files, and adding an assertion in prefill_indexer.py to enforce configuration invariants for INDEXER_SCORE_CAP.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread models/deepseek/v4/config.py Outdated
Comment thread models/deepseek/v4/decode_fwd.py
Comment thread models/deepseek/v4/prefill_fwd.py
Comment thread models/deepseek/v4/prefill_indexer.py
@coderabbitai

coderabbitai Bot commented Jul 2, 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: 078ff8b6-a065-4965-826f-09405b0e234e

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

This PR centralizes static KV/index cache pool sizing (original, compressed, index) as config constants for decode and prefill modes, replacing hardcoded or locally derived values across many attention, compressor, and indexer kernels. Separately, decode and prefill forward drivers stop computing logits via lm_head and instead output hidden states directly.

Changes

Config-driven KV/index cache sizing

Layer / File(s) Summary
New sizing constants and recv floor
models/deepseek/v4/config.py
Adds decode/prefill KV/CMP/IDX block-count constants and raises DECODE_RECV_MAX/PREFILL_RECV_MAX minimum floor from 16 to 1024.
Decode attention kernels
models/deepseek/v4/decode_attention_csa.py, decode_attention_hca.py, decode_attention_swa.py
Import config constants and replace hardcoded ORI/CMP/IDX block counts with config-derived values affecting cache tensor shapes.
Decode compressor and indexer
models/deepseek/v4/decode_compressor_ratio128.py, decode_compressor_ratio4.py, decode_indexer.py, decode_indexer_compressor.py
Switches CMP/IDX cache block counts from local formulas to config-provided constants.
Decode sparse attention variants
models/deepseek/v4/decode_sparse_attn.py, decode_sparse_attn_hca.py, decode_sparse_attn_swa.py
Replaces fixed/derived ORI and CMP block counts with config-driven values.
Prefill attention kernels
models/deepseek/v4/prefill_attention_csa.py, prefill_attention_hca.py, prefill_attention_swa.py
Rewires ORI/CMP/IDX block-count derivations; idx_kv_cache shapes updated to PREFILL_IDX_BLOCK_NUM across kernel and test signatures/specs.
Prefill compressor and indexer
models/deepseek/v4/prefill_compressor_ratio128.py, prefill_compressor_ratio4.py, prefill_indexer.py, prefill_indexer_compressor.py
Sources CMP/IDX cache pool sizing from config instead of local formulas; indexer score caps fixed to new constants.
Prefill layer wiring and sparse attention
models/deepseek/v4/prefill_layer.py, prefill_sparse_attn.py
Updates idx_kv_cache dimensions to PREFILL_IDX_BLOCK_NUM across core/kernel/host functions and cmp_kv shapes to CMP_BLOCK_NUM.

Decode/Prefill forward hidden_out output

Layer / File(s) Summary
Decode forward output change
models/deepseek/v4/decode_fwd.py
Removes lm_head_tp/hidden_norm/logits path; writes hidden_out[r] per rank and updates exported tensor spec from logits (FP32) to hidden_out (BF16).
Prefill forward output change
models/deepseek/v4/prefill_fwd.py
Removes lm-head/logits computation and hidden_norm; updates idx_kv_cache sizing to PREFILL_IDX_BLOCK_NUM, writes hidden_out[r], and updates tensor spec output.

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

Possibly related PRs

Suggested labels: enhancement

Poem

A rabbit hopped through configs deep,
Counting blocks it used to keep,
Now hidden states replace the logit's song,
And cache pools sized just right, not wrong. 🐇
Hop, hop — the KV pools align,
One config source, all kernels shine! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: aligning DeepSeek V4 cache layout constants.
Description check ✅ Passed The description matches the PR’s main changes, including shared cache sizing, hidden-state outputs, and recv-buffer floor updates.
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.

@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.

Actionable comments posted: 2

🧹 Nitpick comments (4)
models/deepseek/v4/config.py (2)

284-285: 🧹 Nitpick | 🔵 Trivial

Recv buffer floor increase (16 → 1024) raises fixed memory footprint.

This is a 64x increase to the minimum recv-buffer size regardless of actual token/expert-derived need, applied to both decode and prefill. Confirm this floor was sized for the current distributed layout's worst case and not just a generously safe round number, since it directly scales per-rank buffer allocation.

🤖 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/config.py` around lines 284 - 285, The recv buffer
minimums in the DeepSeek v4 config are being raised from a small floor to 1024
for both decode and prefill, which increases fixed per-rank memory regardless of
the token-derived sizing. Review the buffer sizing logic around DECODE_RECV_MAX
and PREFILL_RECV_MAX in config.py, confirm the floor is actually required for
the current EP/FLASH distributed layout worst case, and if not, reduce it or tie
it to a justified layout-specific bound instead of a generic round number.

265-270: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefill CMP/IDX block counts bypass the batch-multiply pattern used for ORI.

PREFILL_ORI_BLOCK_NUM follows PREFILL_BATCH * PREFILL_ORI_MAX_BLOCKS, but PREFILL_CMP_BLOCK_NUM/PREFILL_IDX_BLOCK_NUM are set directly to the decode totals (DECODE_CMP_BLOCK_NUM/DECODE_IDX_BLOCK_NUM) rather than PREFILL_BATCH * PREFILL_CMP_MAX_BLOCKS (which would yield 8, not 32). The comment at Lines 256-258 explains this is intentional (shared decode-batch physical pool), but the inconsistent naming/formula pattern between ORI and CMP/IDX is easy to misread as a bug during future maintenance. Consider a clearer name (e.g., PREFILL_CMP_BLOCK_NUM = DECODE_CMP_BLOCK_NUM # shared decode-batch pool, not PREFILL_BATCH-scaled) or an inline comment at each assignment to make the divergence explicit at the point of use.

🤖 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/config.py` around lines 265 - 270, The PREFILL CMP/IDX
block-count assignments in config.py intentionally diverge from the ORI
batch-scaled pattern, so make that explicit where the constants are defined.
Update the PREFILL_CMP_BLOCK_NUM and PREFILL_IDX_BLOCK_NUM assignments near the
PREFILL_ORI_MAX_BLOCKS/PREFILL_ORI_BLOCK_NUM block to clearly indicate they
reuse the decode pool (matching DECODE_CMP_BLOCK_NUM and DECODE_IDX_BLOCK_NUM)
rather than multiplying by PREFILL_BATCH, using either clearer naming or inline
comments. Keep the existing ORI formula unchanged and ensure the intent is
obvious in the same section as PREFILL_CMP_MAX_BLOCKS and
PREFILL_IDX_MAX_BLOCKS.
models/deepseek/v4/decode_attention_csa.py (1)

93-97: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inconsistent migration: ORI_BLOCK_NUM still locally derived instead of using DECODE_ORI_BLOCK_NUM.

CMP_BLOCK_NUM and IDX_CACHE_BLOCK_NUM are now sourced directly from the shared config constants (DECODE_CMP_BLOCK_NUM, DECODE_IDX_BLOCK_NUM), but ORI_BLOCK_NUM is still locally re-derived as B * ORI_MAX_BLOCKS instead of using the sibling constant DECODE_ORI_BLOCK_NUM (already defined in config and used this way in decode_attention_hca.py, decode_sparse_attn_hca.py, and decode_sparse_attn_swa.py). While numerically equivalent today (assuming B == DECODE_BATCH), this leaves one path un-migrated and could silently diverge if the config-side derivation changes.

♻️ Proposed fix
 from config import (
     DECODE_CMP_BLOCK_NUM,
     DECODE_IDX_BLOCK_NUM,
+    DECODE_ORI_BLOCK_NUM,
     IDX_CACHE_MAX_BLOCKS,
     KV_CMP_MAX_BLOCKS,
     KV_ORI_MAX_BLOCKS,
 )
...
 IDX_CACHE_BLOCK_NUM = DECODE_IDX_BLOCK_NUM
 ORI_MAX_BLOCKS = KV_ORI_MAX_BLOCKS
-ORI_BLOCK_NUM = B * ORI_MAX_BLOCKS
+ORI_BLOCK_NUM = DECODE_ORI_BLOCK_NUM
 CMP_MAX_BLOCKS = KV_CMP_MAX_BLOCKS
 CMP_BLOCK_NUM = DECODE_CMP_BLOCK_NUM
🤖 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_attention_csa.py` around lines 93 - 97,
`ORI_BLOCK_NUM` in `decode_attention_csa.py` is still derived locally from `B *
ORI_MAX_BLOCKS` while the other block counts already use shared config
constants. Update the `decode_attention_csa.py` constants block to source
`ORI_BLOCK_NUM` from `DECODE_ORI_BLOCK_NUM`, matching the pattern used in
`decode_attention_hca.py`, `decode_sparse_attn_hca.py`, and
`decode_sparse_attn_swa.py`. Keep the existing `ORI_MAX_BLOCKS` alias if needed,
but ensure `ORI_BLOCK_NUM` is no longer independently computed so all decode
paths stay aligned with config.
models/deepseek/v4/prefill_attention_hca.py (1)

75-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Module-level asserts are now tautological.

SPARSE_ORI_BLOCK_NUM is directly assigned B * SPARSE_ORI_MAX_BLOCKS (line 76) and SPARSE_CMP_BLOCK_NUM is directly assigned PREFILL_CMP_BLOCK_NUM (line 79). The subsequent guards:

assert SPARSE_ORI_BLOCK_NUM == B * SPARSE_ORI_MAX_BLOCKS
assert SPARSE_CMP_BLOCK_NUM == PREFILL_CMP_BLOCK_NUM

now compare each value against the exact expression used to define it, so they can never fail — they no longer catch a real divergence (e.g. if PREFILL_CMP_BLOCK_NUM in config.py were changed to something inconsistent with the geometry these sparse caches actually need). Before this refactor these constants were presumably derived independently via ceil/rounding formulas, and the asserts meaningfully cross-checked that against config; that safety net is now gone.

Consider removing these two asserts (they add no value) or replacing them with a check against an independently-derived expectation (e.g. re-deriving the previous ceil/rounding formula and asserting it still matches the config value), so the guard still catches real config/geometry drift.

🔧 Suggested cleanup
 assert S == COMPRESS_RATIO, "first prefill HCA bring-up targets one ratio-128 prompt chunk"
 assert WIN == BLOCK_SIZE, "prefill HCA currently assumes one window page per batch"
-assert SPARSE_ORI_BLOCK_NUM == B * SPARSE_ORI_MAX_BLOCKS
-assert SPARSE_CMP_BLOCK_NUM == PREFILL_CMP_BLOCK_NUM
🤖 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/prefill_attention_hca.py` around lines 75 - 92, The
module-level assertions in prefill_attention_hca.py are tautological because
SPARSE_ORI_BLOCK_NUM and SPARSE_CMP_BLOCK_NUM are now assigned directly from the
same expressions they assert against. Update the guard logic around
SPARSE_ORI_BLOCK_NUM, SPARSE_CMP_BLOCK_NUM, and PREFILL_CMP_BLOCK_NUM to either
remove the redundant asserts or replace them with independently-derived
expectations that still validate the sparse-cache geometry against config drift.
🤖 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.

Inline comments:
In `@models/deepseek/v4/decode_fwd.py`:
- Around line 700-701: Remove the stale lm_head_weight requirement from the
hidden-state contract in decode_fwd.py: the L3 path now only produces hidden_out
and should no longer accept or order lm_head_weight. Update the relevant
spec/typing declaration and the ordered_names logic to drop "lm_head_weight",
and delete the now-unreachable LM_HEAD_NAMES branch in the contract builder if
nothing else references it. Keep the hidden_out path intact so callers using the
serving-side LM-head flow no longer need to pass the unused tensor.

In `@models/deepseek/v4/prefill_fwd.py`:
- Around line 759-760: The hidden-state contract still exposes a stale
lm_head_weight input even though l3_prefill_fwd now returns hidden_out directly
and no longer uses lm_head_tp. Remove lm_head_weight from the relevant specs and
update ordered_names in prefill_fwd.py so the exported contract matches the new
serving-side path; also clean up the LM_HEAD_NAMES branch if it is now
unreachable.

---

Nitpick comments:
In `@models/deepseek/v4/config.py`:
- Around line 284-285: The recv buffer minimums in the DeepSeek v4 config are
being raised from a small floor to 1024 for both decode and prefill, which
increases fixed per-rank memory regardless of the token-derived sizing. Review
the buffer sizing logic around DECODE_RECV_MAX and PREFILL_RECV_MAX in
config.py, confirm the floor is actually required for the current EP/FLASH
distributed layout worst case, and if not, reduce it or tie it to a justified
layout-specific bound instead of a generic round number.
- Around line 265-270: The PREFILL CMP/IDX block-count assignments in config.py
intentionally diverge from the ORI batch-scaled pattern, so make that explicit
where the constants are defined. Update the PREFILL_CMP_BLOCK_NUM and
PREFILL_IDX_BLOCK_NUM assignments near the
PREFILL_ORI_MAX_BLOCKS/PREFILL_ORI_BLOCK_NUM block to clearly indicate they
reuse the decode pool (matching DECODE_CMP_BLOCK_NUM and DECODE_IDX_BLOCK_NUM)
rather than multiplying by PREFILL_BATCH, using either clearer naming or inline
comments. Keep the existing ORI formula unchanged and ensure the intent is
obvious in the same section as PREFILL_CMP_MAX_BLOCKS and
PREFILL_IDX_MAX_BLOCKS.

In `@models/deepseek/v4/decode_attention_csa.py`:
- Around line 93-97: `ORI_BLOCK_NUM` in `decode_attention_csa.py` is still
derived locally from `B * ORI_MAX_BLOCKS` while the other block counts already
use shared config constants. Update the `decode_attention_csa.py` constants
block to source `ORI_BLOCK_NUM` from `DECODE_ORI_BLOCK_NUM`, matching the
pattern used in `decode_attention_hca.py`, `decode_sparse_attn_hca.py`, and
`decode_sparse_attn_swa.py`. Keep the existing `ORI_MAX_BLOCKS` alias if needed,
but ensure `ORI_BLOCK_NUM` is no longer independently computed so all decode
paths stay aligned with config.

In `@models/deepseek/v4/prefill_attention_hca.py`:
- Around line 75-92: The module-level assertions in prefill_attention_hca.py are
tautological because SPARSE_ORI_BLOCK_NUM and SPARSE_CMP_BLOCK_NUM are now
assigned directly from the same expressions they assert against. Update the
guard logic around SPARSE_ORI_BLOCK_NUM, SPARSE_CMP_BLOCK_NUM, and
PREFILL_CMP_BLOCK_NUM to either remove the redundant asserts or replace them
with independently-derived expectations that still validate the sparse-cache
geometry against config drift.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 620632a4-c961-459d-a56f-e8c67ff73eb3

📥 Commits

Reviewing files that changed from the base of the PR and between 1b9a997 and bab88ea.

📒 Files selected for processing (22)
  • models/deepseek/v4/config.py
  • models/deepseek/v4/decode_attention_csa.py
  • models/deepseek/v4/decode_attention_hca.py
  • models/deepseek/v4/decode_attention_swa.py
  • models/deepseek/v4/decode_compressor_ratio128.py
  • models/deepseek/v4/decode_compressor_ratio4.py
  • models/deepseek/v4/decode_fwd.py
  • models/deepseek/v4/decode_indexer.py
  • models/deepseek/v4/decode_indexer_compressor.py
  • models/deepseek/v4/decode_sparse_attn.py
  • models/deepseek/v4/decode_sparse_attn_hca.py
  • models/deepseek/v4/decode_sparse_attn_swa.py
  • models/deepseek/v4/prefill_attention_csa.py
  • models/deepseek/v4/prefill_attention_hca.py
  • models/deepseek/v4/prefill_attention_swa.py
  • models/deepseek/v4/prefill_compressor_ratio128.py
  • models/deepseek/v4/prefill_compressor_ratio4.py
  • models/deepseek/v4/prefill_fwd.py
  • models/deepseek/v4/prefill_indexer.py
  • models/deepseek/v4/prefill_indexer_compressor.py
  • models/deepseek/v4/prefill_layer.py
  • models/deepseek/v4/prefill_sparse_attn.py

Comment thread models/deepseek/v4/decode_fwd.py Outdated
Comment thread models/deepseek/v4/prefill_fwd.py Outdated
high-cloud added a commit to high-cloud/pypto-lib that referenced this pull request Jul 2, 2026
@high-cloud high-cloud force-pushed the fix/dsv4-cache-layout-constants branch from e85d2ee to 50e316b Compare July 2, 2026 09:23
Define shared paged-cache block counts in config.py and use them across decode and prefill attention, compressor, sparse-attn, and indexer paths.

Size prefill compressed KV and index KV pools from the same decode-batch physical layout while keeping per-table max-block widths explicit.

Update L3 prefill/decode forward kernels to emit hidden states for the serving-side lm_head path, drop the stale lm_head_weight ABI, and raise the MoE recv-buffer floor for the current distributed layout.
@high-cloud high-cloud force-pushed the fix/dsv4-cache-layout-constants branch from 50e316b to b496207 Compare July 2, 2026 10:40
@zhangqi-chen zhangqi-chen merged commit 97cf255 into hw-native-sys:main Jul 2, 2026
5 of 7 checks passed
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.

2 participants