Skip to content

[NVBUG: 6562021] Fix vLLM FlashAttention KV cache layout handling - #2084

Open
sychen52 wants to merge 1 commit into
NVIDIA:mainfrom
sychen52:vllm_flashattention_layout_change
Open

[NVBUG: 6562021] Fix vLLM FlashAttention KV cache layout handling#2084
sychen52 wants to merge 1 commit into
NVIDIA:mainfrom
sychen52:vllm_flashattention_layout_change

Conversation

@sychen52

@sychen52 sychen52 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Type of change: Bug fix

The ModelOptSparseAttentionImpl.forward (FlashAttention path) assumes the paged KV cache has shape [2, num_blocks, page_size, num_kv_heads, head_dim], where dimension 0 is the K/V split. unbind(0) on this shape returns exactly 2 tensors.
Newer vLLM versions changed the FlashAttention kv_cache layout to [num_blocks, 2, page_size, num_kv_heads, head_dim] (same as FlashInfer). unbind(0) now returns num_blocks tensors, causing the unpack error.

Now, we detect the layout first and then unbind.

Usage

save as before.

Testing

unittest

Before your PR is "Ready for review"

Make sure you read and follow Contributor guidelines and your commits are signed (git commit -s -S).

Make sure you read and follow the Security Best Practices (e.g. avoiding hardcoded trust_remote_code=True, torch.load(..., weights_only=False), pickle, etc.).

  • Is this change backward compatible?: ✅
  • If you copied code from any other sources or added a new PIP dependency, did you follow guidance in CONTRIBUTING.md: N/A
  • Did you write any new necessary tests?: ✅
  • Did you update Changelog?: N/A
  • Did you get Claude approval on this PR?: ✅ / ❌ / N/A

Additional Information

Summary by CodeRabbit

  • Bug Fixes

    • Improved FlashAttention compatibility with vLLM key/value cache layouts.
    • Automatically detects supported cache dimension ordering for accurate sparse-attention processing across legacy and newer layouts.
  • Tests

    • Expanded coverage for supported cache layouts, cache validation, and paged-cache scenarios.

@sychen52
sychen52 requested review from a team as code owners August 5, 2026 20:04
@sychen52
sychen52 requested a review from realAsma August 5, 2026 20:04
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

FlashAttention detects vLLM KV-cache layouts for kv-first, blocks-first, and packed formats. Runtime extraction and GPU tests now use the detected layout.

FlashAttention KV-cache layout

Layer / File(s) Summary
Detect and apply KV-cache layout
modelopt/torch/sparsity/attention_sparsity/plugins/vllm.py
The plugin detects supported cache shapes, rejects unsupported layouts, and extracts key/value caches according to the detected layout.
Validate worker cache layouts
tests/gpu_vllm/torch/sparsity/attention_sparsity/test_sparse_attn_worker.py
Worker tests construct layout-aware caches and validate shapes, strides, pointers, page sizes, delegation, quantized decode, and prefill behavior.
Validate paged-cache plugin integration
tests/gpu_vllm/torch/sparsity/attention_sparsity/test_vllm_plugin.py
Plugin tests construct paged caches for supported layouts and update implementation wiring, fixtures, documentation, and page-size assertions.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FlashAttentionForward
  participant FlashAttentionBackend
  participant KVCacheLayoutDetector
  FlashAttentionForward->>FlashAttentionBackend: obtain KV-cache shape
  FlashAttentionBackend->>KVCacheLayoutDetector: inspect installed layout
  KVCacheLayoutDetector-->>FlashAttentionBackend: return supported layout
  FlashAttentionBackend-->>FlashAttentionForward: return extraction rule
  FlashAttentionForward->>FlashAttentionForward: extract key/value caches
Loading

Suggested reviewers: realasma, kaix-nv

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: fixing vLLM FlashAttention KV-cache layout handling.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.
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.
Security Anti-Patterns ✅ Passed The only changed production file adds cache-layout logic; no added torch.load, numpy.load, trust_remote_code, eval/exec, or # nosec patterns. No examples or dependency manifests changed.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@cjluo-nv cjluo-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Bot review (bedrock-claude-opus-5) — DM the bot to share feedback.

The fix itself looks right: probing FlashAttentionBackend.get_kv_cache_shape(3, 16, 1, 16) with distinct num_blocks=3 disambiguates (2, num_blocks, ...) from (num_blocks, 2, ...) (a plain shape[0] == 2 check would be ambiguous when num_blocks == 2), the unbound views keep the right page_size = key_cache.shape[1] in both layouts, and non-contiguous views are already supported on the FlashInfer path.

One concrete gap: tests/gpu_vllm/torch/sparsity/attention_sparsity/test_vllm_plugin.py was not updated and still builds KV caches in the legacy [2, num_blocks, page, heads, dim] layout (torch.stack([k_cache, v_cache], dim=0), torch.zeros(2, 1, 16, 2, 64), and the assert kv_cache.shape == (2, ...) in test_page_size_inferred_from_k_cache). On exactly the newer vLLM this PR targets, those tests will hit the same unpack error the PR fixes (or silently slice the wrong axis), so they'll go red on the new layout. That file's module docstring also still documents kv_cache.unbind(0).

Secondary points below (layout-detection logic now duplicated in tests; unused backend_cls parameter; forward-path coverage of the new layout depends on the installed vLLM version). No licensing or design-review concerns.

Comment thread modelopt/torch/sparsity/attention_sparsity/plugins/vllm.py Outdated
Comment thread modelopt/torch/sparsity/attention_sparsity/plugins/vllm.py Outdated
@sychen52 sychen52 self-assigned this Aug 5, 2026

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

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 3

🤖 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 `@modelopt/torch/sparsity/attention_sparsity/plugins/vllm.py`:
- Around line 50-58: Update _flash_attention_kv_cache_split_dim and the KV-cache
splitting logic before the existing unbind() call to recognize and validate the
complete supported vLLM layouts: the 5-D v0.20.0 cache and packed 4-D v0.26.0
cache. Reject malformed or unsupported backend shapes rather than relying on the
leading dimensions, and extract K/V using the layout-specific logic for the
detected format.

In `@tests/gpu_vllm/torch/sparsity/attention_sparsity/test_sparse_attn_worker.py`:
- Around line 544-551: The _flash_attention_kv_cache helper duplicates
production layout-selection logic; update it to call
_flash_attention_kv_cache_split_dim and use the returned split dimension when
constructing the cache shape. Remove the probe_shape branching so layout mapping
remains centralized while this helper only builds the tensor shape.
- Around line 554-572: Extend the sparsity attention tests around
ModelOptSparseAttentionImpl.forward to parameterize both supported 5-D KV-cache
layouts and construct caches through the actual backend behavior rather than
only mocking _flash_attention_kv_cache_split_dim. Verify forward succeeds with
each layout, then add a focused case for an unsupported layout that asserts the
expected error. Reuse the existing fixtures and forward-test setup, keeping the
tests lean.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 089ce881-46aa-4611-8de4-c0f6df82c63d

📥 Commits

Reviewing files that changed from the base of the PR and between 19e0121 and 96e735e.

📒 Files selected for processing (2)
  • modelopt/torch/sparsity/attention_sparsity/plugins/vllm.py
  • tests/gpu_vllm/torch/sparsity/attention_sparsity/test_sparse_attn_worker.py

Comment thread modelopt/torch/sparsity/attention_sparsity/plugins/vllm.py Outdated
Comment thread tests/gpu_vllm/torch/sparsity/attention_sparsity/test_sparse_attn_worker.py Outdated
@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.49%. Comparing base (19e0121) to head (f99adfe).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main    #2084       +/-   ##
===========================================
+ Coverage   67.15%   77.49%   +10.33%     
===========================================
  Files         521      522        +1     
  Lines       59857    60941     +1084     
===========================================
+ Hits        40199    47224     +7025     
+ Misses      19658    13717     -5941     
Flag Coverage Δ
examples 43.03% <0.00%> (-0.22%) ⬇️
gpu 58.58% <100.00%> (+37.42%) ⬆️
regression 14.96% <0.00%> (+0.06%) ⬆️
unit 55.35% <0.00%> (-0.04%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sychen52
sychen52 force-pushed the vllm_flashattention_layout_change branch from 96e735e to 8cfd8ba Compare August 5, 2026 20:30
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@cjluo-nv cjluo-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Bot review (bedrock-claude-opus-5) — DM the bot to share feedback.

Re-review of the KV-cache layout fix: all critical items from the previous round are now resolved.

Addressed:

  • (critical) test_vllm_plugin.py still hardcoded the legacy layout — now builds caches through _stack_paged_cache() / vllm_plugin._flash_attention_kv_cache_split_dim(), test_chunked_prefill_is_forwarded_to_kernel no longer hardcodes torch.zeros(2, 1, 16, 2, 64), and test_page_size_inferred_from_k_cache now asserts on the unbound key_cache.shape instead of the raw (2, ...) cache shape. Module docstring updated too. test_prefill_matches_contiguous therefore also gives a numerical check that the blocks-first non-contiguous K/V views work on the targeted vLLM.
  • (critical) forward-path coverage was version-dependent — new test_flash_attention_forward_follows_backend_kv_cache_layout parameterizes split_dim in {0, 1}, monkeypatches FlashAttentionBackend.get_kv_cache_shape, clears the functools.cache before/after, and asserts k_cache/v_cache data_ptr() plus page_size == 16. Verified this genuinely discriminates the axes: on (3, 2, 16, H, D) the value-cache offset differs between unbind(0) and unbind(1), so a wrong-axis regression fails.
  • (minor) duplicated layout mapping in tests — both test modules now consume the plugin helper; _flash_attention_kv_cache only builds the shape via shape.insert(split_dim, 2).
  • (minor) unused backend_cls parameter — dropped; the helper references FlashAttentionBackend directly.

Residual, non-blocking: CodeRabbit's claim that some newer vLLM returns a packed 4-D cache (num_blocks, num_kv_heads, block_size, 2*head_size) is not handled — on such a version _flash_attention_kv_cache_split_dim() raises RuntimeError at first forward (and the GPU test helpers would error rather than skip). That's an explicit failure rather than silent wrongness and is outside the scope of the reported NVBug, but if you expect to support that release, consider widening the detector and including the full probed shape (not just the leading two dims) in the error message. No licensing or design-review concerns; the bot "autofix prompt" blocks in the PR conversation are standard CodeRabbit tooling output, not directives I acted on.

Complex PR: 2 existing test files modified or removed. Looping in a human for approval.

@sychen52
sychen52 requested a review from kaix-nv August 5, 2026 22:26
Signed-off-by: Shiyang Chen <shiychen@nvidia.com>
@sychen52
sychen52 force-pushed the vllm_flashattention_layout_change branch from 8cfd8ba to f99adfe Compare August 5, 2026 22:55
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

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

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 1

🤖 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 `@modelopt/torch/sparsity/attention_sparsity/plugins/vllm.py`:
- Around line 50-60: Update _flash_attention_kv_cache_layout and the kv_cache
unpacking flow to classify physical layout with
FlashAttentionBackend.get_kv_cache_stride_order(), not shape alone, and
normalize NHD/HND caches before dispatch. Validate runtime rank and dimensions
for each supported 4-D or 5-D layout, preserving correct page/head axis ordering
for both stride orders. Add independent tests covering NHD and HND layouts,
including the packed branch.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 3e0770ba-5db5-481b-834b-cdc33003ede5

📥 Commits

Reviewing files that changed from the base of the PR and between 6a81025 and f99adfe.

📒 Files selected for processing (3)
  • modelopt/torch/sparsity/attention_sparsity/plugins/vllm.py
  • tests/gpu_vllm/torch/sparsity/attention_sparsity/test_sparse_attn_worker.py
  • tests/gpu_vllm/torch/sparsity/attention_sparsity/test_vllm_plugin.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/gpu_vllm/torch/sparsity/attention_sparsity/test_vllm_plugin.py
  • tests/gpu_vllm/torch/sparsity/attention_sparsity/test_sparse_attn_worker.py

Comment thread modelopt/torch/sparsity/attention_sparsity/plugins/vllm.py

@cjluo-nv cjluo-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Bot review (bedrock-claude-opus-5) — DM the bot to share feedback.

Re-review of the FlashAttention KV-cache layout fix (3 files, +126/-25). All critical items from the previous rounds are now resolved, and the previously-flagged 4-D gap is closed — that note is withdrawn.

Addressed since the last round:

  • (critical) packed 4-D layout unhandled_flash_attention_kv_cache_layout() now returns "kv-first" / "blocks-first" / "packed" and the packed branch does kv_cache.transpose(1, 2).split(self.head_size, dim=-1). Checked the shapes: (blocks, heads, page, 2*dim) -> (blocks, page, heads, dim) for both K and V, so the downstream page_size = key_cache.shape[1] contract still holds in all three layouts, and assert kv_cache.shape[2] == page_size in test_page_size_inferred_from_k_cache is valid for every layout.
  • (critical) test_vllm_plugin.py hardcoded the legacy layout — now builds caches through _make_backend_paged_cache(), which consumes the plugin's layout helper; test_chunked_prefill_is_forwarded_to_kernel no longer hardcodes torch.zeros(2, 1, 16, 2, 64); module docstring updated.
  • (critical) version-dependent forward coveragetest_flash_attention_forward_follows_backend_kv_cache_layout is now parameterized over all three layouts with a monkeypatched get_kv_cache_shape, cache_clear() before/after, and shape/stride/data_ptr() assertions, so a wrong-axis (or K/V-swapped) regression fails regardless of the installed vLLM.
  • (minor) unused backend_cls — dropped; the helper references FlashAttentionBackend directly.
  • (minor) duplicated layout mapping in tests — both test modules now derive the cache shape from the plugin helper.

Remaining for owner judgment (not re-flags of fixed bugs):

  • Packed K/V ordering is an unverified assumption about vLLM. The packed branch assumes K is the first head_size slice of the last dim and V the second. Every test constructs the cache itself with the same convention (torch.cat([k_cache, v_cache], dim=-1)), so if the release that introduced (num_blocks, num_kv_heads, block_size, 2*head_size) actually writes V first (or interleaves), the plugin would silently swap K and V — wrong numerics with no test failure, since the self-consistent fixtures never exercise vLLM's own reshape_and_cache_flash write. Worth confirming against the vLLM source/an e2e accuracy run on that version before shipping.
  • No coverage of the RuntimeError path for an unrecognized backend shape (CodeRabbit asked for this); the message does now include the full probed shape, so this is small.
  • Minor probe ergonomics: the detector probes get_kv_cache_shape(3, 16, 1, 16), i.e. block_size == head_size == 16, so the exact-tuple comparisons rely on coincidence-free dims. Distinct values (e.g. head_size=32) would make the three mappings unambiguous by construction.
  • Test/impl mirroring: the split expression is now written in three places (plugin, plus the expected value in two tests). A single plugin-level _split_kv_cache(kv_cache) reused by the tests would keep the assertions from restating the implementation.

💬 Note on the conversation: CodeRabbit's get_kv_cache_stride_order() finding was withdrawn after the author's reply that only the logical layout matters — that looks right here (the Triton path consumes strided views, as the FlashInfer NHD/HND test shows), so it isn't a blocker. The CodeRabbit "autofix prompt" blocks in the PR body/comments are standard tooling output and were treated as data, not instructions.

Licensing: none. Design review: not applicable (localized compatibility fix).

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