Skip to content

fix(ds4): reserve full B12X compressed MLA workspace envelope - #227

Open
joninco wants to merge 2 commits into
local-inference-lab:dev/gilded-gnosisfrom
joninco:codex/gilded-gnosis-ds4-compressed-mla-workspace-20260802
Open

fix(ds4): reserve full B12X compressed MLA workspace envelope#227
joninco wants to merge 2 commits into
local-inference-lab:dev/gilded-gnosisfrom
joninco:codex/gilded-gnosis-ds4-compressed-mla-workspace-20260802

Conversation

@joninco

@joninco joninco commented Aug 2, 2026

Copy link
Copy Markdown

Closes #224.

Summary

Prevent the DeepSeek-V4 B12X compressed-MLA path from requesting a larger v1 workspace after lock_workspace().

The profiling call already reached _reserve_dummy_compressed_mla_scratch; the failure was not a skipped warmup. The reserve planned only the single rows=max_num_batched_tokens point, while Sparkinfer's split contract can require more total q-chunk scratch at a smaller runtime row count. This change reserves the complete row/split envelope before graph capture and keeps runtime workspace growth disabled.

It also makes the reservation geometry match runtime for:

  • causal and DSpark non-causal SWA widths;
  • C1, C4, and C128 compression;
  • decode and chunked-prefill row regimes;
  • DCP gathered-head capacity.

Confirmed root cause

Instrumentation against the exact reporter image showed that the reserve branch ran:

[GEOMETRY] reserve rows=2048 swa_width=128 indexed_width=4096
width=4224 splits=5 bytes=321.50 MiB
[WORKSPACE DEBUG] Resized workspace from
'b12x.py:563:_reserve_dummy_compressed_mla_scratch':
0.00 MB -> 321.50 MB (ubatch 0, lane 0)
[WORKSPACE DEBUG] Workspace locked. Current sizes: [321.5029296875]

The failing runtime shape used the same width but a different split regime:

[GEOMETRY] runtime mode=extend rows=232 swa_width=128
indexed_width=4096 width=4224 splits=66 bytes=480.40 MiB

Sparkinfer's scratch contains both row-sized buffers and split intermediates. The latter are governed by:

q_chunks = rows * split_chunks_for_contract(rows, width, max_chunks)

That product is not maximized at rows=max_num_batched_tokens. At width 4224, the split count falls from 66 for small chunks to 5 at 2048 rows, so the original single-point reserve under-allocated even though runtime and reserve widths matched.

For the 1,048,576-token configuration in #224, real-planner instrumentation gives:

C128 causal width=8320:
  old reserve     578.502930 MiB
  runtime maximum 1044.096680 MiB at rows=256
  new reserve     1044.315430 MiB

C128 DSpark width=8704:
  old reserve     578.502930 MiB
  runtime maximum 1092.284180 MiB at rows=256
  new reserve     1092.502930 MiB

The reported 607.70 MiB and 917.67 MiB requests are both inside the newly reserved envelope.

Changes

  • Sweep every reachable row count during profiling and pass the resulting max_q_chunks capacity to the Sparkinfer plan.
  • Use the same split-cap helper in reservation and runtime planning.
  • Reserve DSpark's padded non-causal SWA index width rather than only the causal window width.
  • Reserve gathered DCP heads, matching the runtime query all-gather.
  • Share C128 and DSpark width helpers between metadata construction and workspace reservation to prevent geometry drift.
  • Add a CPU-only real-Sparkinfer regression test that asserts reserve_bytes >= max(runtime_bytes) over every row count for C1/C4/C128, causal/DSpark, and DCP2 cases.

No workspace is unlocked or resized after capture. Kernel inputs, attention math, and output numerics are unchanged.

Validation

Commands run from the PR branch:

.venv/bin/python -m pytest tests/models/deepseek_v4/test_b12x_compressed_mla_workspace.py -v
# 9 passed, 7 skipped (real-Sparkinfer cases are unavailable in the local venv)

.venv/bin/python -m pytest \
  tests/model_executor/layers/test_sparse_attn_indexer_b12x.py \
  tests/model_executor/test_b12x_sparse_indexer_warmup.py -q
# 33 passed

pre-commit run --from-ref origin/dev/gilded-gnosis --to-ref HEAD
# all applicable hooks passed, including ruff, mypy, SPDX, and static checks

The compressed-MLA test was also run inside the exact reporter image with the four patched source files bind-mounted over site-packages:

voipmonitor/vllm:gilded-gnosis-v20-vllm1e9c9c3-sieec30ff-fi801d57a-cu132-20260731-r16
/opt/venv/bin/python -m pytest /tmp/test_b12x_compressed_mla_workspace.py -v
# 16 passed

That image run exercised the real sparkinfer.attention.compressed_mla.plan() on CPU and swept all runtime row counts without allocating GPU scratch.

Model/serving evaluation

A full post-fix DeepSeek-V4 long-context serving run was not performed because the available GPUs were already occupied at approximately 91 GiB/GPU. This patch changes allocation capacity only; it does not change model outputs or numerical kernels. The exact-image real-planner envelope test above validates the failure mechanism and the post-lock allocation bound. A human-run long-context smoke test should be completed before marking this PR ready.

Duplicate-work check

Searched open PRs for compressed MLA workspace, DeepSeek V4 B12X workspace, workspace locked b12x, and references to #224. No PR implements this fix.

PR #161 references the issue number in search results but is materially different: it adds selected-record sparse-CKV decode under DCP and does not modify the DeepSeek-V4 compressed-MLA planner or reserve path.

AI assistance and accountability

AI assistance was used for instrumentation, implementation, test construction, and review: OpenAI Codex performed the primary work, and Anthropic Claude/Fable provided independent review. The final Fable review found no substantive issues after follow-up revisions.

Before moving this PR out of draft, the human submitter must review every changed line, understand and be able to defend the change end-to-end, and run the relevant long-context serving validation.

Summary by CodeRabbit

  • Bug Fixes

    • Improved DeepSeek V4 B12x compressed attention workspace reservation across compression, speculative decoding, and context-parallel configurations.
    • Improved workspace sizing for large query chunks and sliding-window attention, helping prevent insufficient or excessive scratch-space allocation.
    • Standardized compressed-attention geometry and metadata sizing for more consistent runtime behavior.
  • Tests

    • Added comprehensive coverage for workspace dimensions, head gathering, geometry calculations, caching, and runtime plan comparisons.

Assisted-by: Claude

Assisted-by: OpenAI Codex

Signed-off-by: joninco <jon@bullpoint.org>
Share capacity calculations across reservation and metadata paths, cover the split-contract row envelope, and tighten the real-planner regression test.

Assisted-by: Claude Fable

Assisted-by: OpenAI Codex

Signed-off-by: joninco <jon@bullpoint.org>
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the vLLM project.

💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in #pr-reviews, coordinate on features in #feat- channels, or join special interest groups in #sig- channels.

PRs do not trigger a full CI run by default. Once the PR is approved and ready to go, your PR reviewer(s) can run CI to test the changes comprehensively before merging.

To run CI, PR reviewers can either: Add ready label to the PR or enable auto-merge.

If you have any questions, please reach out to us on Slack at https://slack.vllm.ai.

Agent Guidelines

IMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban.

🚀

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Compressed MLA workspace geometry

Layer / File(s) Summary
Shared geometry helpers and consumers
vllm/v1/attention/backends/mla/compressor_utils.py, vllm/v1/attention/backends/mla/sparse_swa.py, vllm/models/deepseek_v4/sparse_mla.py
Added shared helpers for aligned C128A widths, DSpark SWA widths, split caps, and maximum query chunks. Updated sparse attention calculations to use them.
B12X runtime workspace reservation
vllm/models/deepseek_v4/nvidia/b12x.py
Updated scratch reservation geometry for compression, DSpark, DCP head counts, split caps, and query-chunk capacity.
Workspace reservation validation
tests/models/deepseek_v4/test_b12x_compressed_mla_workspace.py
Added mocked and installed-Sparkinfer tests for workspace widths, query-chunk envelopes, DCP head gathering, caching, and runtime plan coverage.

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

Sequence Diagram(s)

sequenceDiagram
  participant B12x
  participant GeometryHelpers
  participant SparkinferPlanner
  participant WorkspaceManager
  B12x->>GeometryHelpers: calculate compressed widths and query chunks
  B12x->>SparkinferPlanner: request scratch plan
  SparkinferPlanner-->>B12x: return runtime workspace requirements
  B12x->>WorkspaceManager: reserve workspace envelope
Loading

Possibly related PRs

Suggested reviewers: lukealonso

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary fix: reserving the full B12X compressed MLA workspace envelope.
Linked Issues check ✅ Passed The changes address [#224] by reserving the full runtime workspace envelope and adding regression coverage for affected configurations.
Out of Scope Changes check ✅ Passed The implementation changes and tests remain focused on B12X compressed MLA workspace reservation and its shared geometry calculations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@joninco
joninco marked this pull request as ready for review August 2, 2026 12:54

@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)
vllm/v1/attention/backends/mla/compressor_utils.py (1)

16-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add Args/Returns sections to the new public helpers.

get_c128a_topk_width, get_dspark_swa_index_width, and get_compressed_mla_max_q_chunks have one-line docstrings without Args:/Returns: sections. get_compressed_mla_split_cap has no docstring at all. These four functions form the new shared geometry contract consumed by sparse_mla.py, sparse_swa.py, and b12x.py, so documenting their parameters and return values helps downstream maintainers.

📝 Proposed docstring additions
 def get_c128a_topk_width(max_model_len: int, compress_ratio: int) -> int:
-    """Return C128 indexed width padded for FlashMLA B_TOPK divisibility."""
+    """Return C128 indexed width padded for FlashMLA B_TOPK divisibility.
+
+    Args:
+        max_model_len: Maximum model context length in tokens.
+        compress_ratio: Compression ratio applied to the KV cache.
+
+    Returns:
+        The compressed top-k width, rounded up to the alignment boundary.
+    """
     compressed_width = cdiv(max_model_len, compress_ratio)
     return cdiv(compressed_width, _C128A_TOPK_ALIGNMENT) * _C128A_TOPK_ALIGNMENT


 def get_dspark_swa_index_width(
     window_size: int,
     num_speculative_tokens: int,
 ) -> int:
-    """Return the padded width of non-causal DSpark SWA indices."""
+    """Return the padded width of non-causal DSpark SWA indices.
+
+    Args:
+        window_size: Sliding-window attention size.
+        num_speculative_tokens: Number of speculative tokens for DSpark.
+
+    Returns:
+        The padded non-causal index width.
+    """
     width = max(int(window_size), 0) + max(int(num_speculative_tokens), 0)
     return cdiv(width, _DSPARK_SWA_INDEX_ALIGNMENT) * _DSPARK_SWA_INDEX_ALIGNMENT


 def get_compressed_mla_split_cap(width: int) -> int:
+    """Return the maximum split-chunk cap for a given compressed-MLA width.
+
+    Args:
+        width: Combined SWA and indexed width.
+
+    Returns:
+        The split-chunk cap.
+    """
     return max(1, cdiv(max(int(width), 1), _COMPRESSED_MLA_SPLIT_ALIGNMENT))

As per coding guidelines, "Use Google-style docstrings in Python code, with Args:/Returns:/Raises: sections instead of reStructuredText/Sphinx fields such as :param:, :return:, and :rtype:."

🤖 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 `@vllm/v1/attention/backends/mla/compressor_utils.py` around lines 16 - 54,
Update the docstrings for get_c128a_topk_width, get_dspark_swa_index_width,
get_compressed_mla_split_cap, and get_compressed_mla_max_q_chunks to use
Google-style Args and Returns sections describing each parameter and the integer
value returned; preserve the existing behavior and calculations.

Source: Coding guidelines

🤖 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 `@vllm/v1/attention/backends/mla/compressor_utils.py`:
- Around line 16-54: Update the docstrings for get_c128a_topk_width,
get_dspark_swa_index_width, get_compressed_mla_split_cap, and
get_compressed_mla_max_q_chunks to use Google-style Args and Returns sections
describing each parameter and the integer value returned; preserve the existing
behavior and calculations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ab88d2ff-4211-4446-889a-a834c3d24409

📥 Commits

Reviewing files that changed from the base of the PR and between 3003860 and baa820a.

📒 Files selected for processing (5)
  • tests/models/deepseek_v4/test_b12x_compressed_mla_workspace.py
  • vllm/models/deepseek_v4/nvidia/b12x.py
  • vllm/models/deepseek_v4/sparse_mla.py
  • vllm/v1/attention/backends/mla/compressor_utils.py
  • vllm/v1/attention/backends/mla/sparse_swa.py

@voipmonitor

Copy link
Copy Markdown

Functional review is clean. I reproduced the reported planner failure exactly: the old reservation plans 321.50 MiB at 2,048 rows, while the runtime peak is 480.40 MiB at 232 rows because the split count rises to 66. The PR test set passes 16/16 against the installed SparkInfer planner, and a TP2/DCP1/K5 long-prompt E2E run completed without a workspace assertion.

I also addressed the remaining CodeRabbit docstring nit in commit voipmonitor@6d8687d. GitHub did not permit maintainer push to the contributor fork, so this commit can be cherry-picked onto the PR branch; it is documentation-only.

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