Skip to content

[BugFix][Attention] Isolate PCP global RoPE runtime buffers - #16311

Closed
zhao-stack wants to merge 2 commits into
vllm-project:mainfrom
zhao-stack:codex/fix-dspark-pcp-rope-buffer-20260911
Closed

zhao-stack wants to merge 2 commits into
vllm-project:mainfrom
zhao-stack:codex/fix-dspark-pcp-rope-buffer-20260911

Conversation

@zhao-stack

@zhao-stack zhao-stack commented Sep 11, 2026 •

Copy link
Copy Markdown
Collaborator

What this PR does / why we need it?

Fix RoPE runtime-buffer aliasing between PCP's canonical global KV-update metadata and rank-local attention metadata.

With DSpark verification, short query rows can put both builds on the cached RoPE path even though their positions differ. Each PCP cache-group builder constructs global metadata, then local metadata. Local RoPE proxies are reused through the step's shared metadata, but a later cache group's global build writes the same process-wide runtime buffers again. The local proxy can then contain global-position values, corrupting target hidden states, generated tokens and speculative acceptance.

Each PCP global metadata builder now owns a persistent RoPE buffer map keyed by configuration and group. Each cos/sin pair is allocated once at the full registered capacity; subsequent calls use the existing in-place gather. Addresses remain stable from small warmup/capture batches through larger replay batches. Local/default, uncached prefill and speculative draft-buffer behavior is preserved; no global cache disabling, per-step cloning or version-specific branch is needed.

Buffer lifetime follows the global builder. Extra storage per requested configuration/group per global builder is 2 * max_num_batched_tokens * rotary_dim * element_size; the global Tensor-position path requests only the default group. Distinct PCP cache-group builders own separate maps. Non-PCP paths allocate no extra buffers.

This independent fix branch starts from upstream main f4b6bd05f05717e10e4521a78f4b3beba5bb5a99. It is separate from main2main PR #16009 and changes none of #16009, #15627, #15965 or #16216. The PR-wide diff contains only three production files and two UT files; pins, release tags, skips, thresholds, golden data and e2e sources are unchanged. The shared-buffer structure predates the historical upgrade experiment; there is no evidence that #16009 or 6f3c34a introduced it.

Does this PR introduce any user-facing change?

Correct position encodings and generation/acceptance behavior for affected PCP + DSpark batches. No configuration or public API change.

How was this patch tested?

Current branch validation — actual pytest logs inspected:

Validation vLLM main b2f685834a6456197e7033966fdef52a23f1abcd release v0.28.0 / 2cf0a6915ce544dc493a0990f2ea38d81601128a
RoPE + DSA unit suites 56 passed 56 passed
Original four-card PCP DSpark e2e 1 passed, 525.72 s 1 passed, 524.02 s

The e2e run was triggered once with the repository's supported comment syntax:

/e2e tests/e2e/pull_request/four_card/context_parallel/test_deepseek_v4.py::test_deepseek_v4_dsa_pcp_dspark

The logs confirm the exact pytest node, model, version checkouts, full graph capture and final passing pytest summaries. Original model, prompts, parameters, prefix assertions, acceptance thresholds and 0.03 tolerance remain intact. No probes, controlled admission, extra test plugins or assertion changes were committed. The passing test does not print final aggregate acceptance rates; periodic metric-log rates are not substituted for the assertion's aggregate metrics.

Commit provenance: the e2e workflow pins fix commit c14993202b111e5c496dc4ffa8ce946e226f4fe7 and rebases it on fixed upstream snapshot fa9f8a7f3d57d28122109dd51a50d91d9f53a805. Final head ca169f36aef921203a9857cb15cbf12a4ef695a1 only adds UT dictionary annotations required by mypy. All production files and the original e2e source are byte-identical between these commits; this is not presented as a final-head e2e invocation. The two 56-test suites were rerun after that annotation follow-up, and the remote tested tracked tree was verified against the final signed commit.

The new numerical builder regression fails on an independent, unmodified upstream-base checkout: 12/16 elements differ after global/local builds (maximum absolute difference 1.088031530380249). It passes with the fix. Coverage includes global → local → next global cache-group builds, preservation of both outputs, repeated calls, full-capacity growth, address stability, multiple RoPE configurations/groups, and unchanged uncached/draft paths.

pytest -q tests/ut/ops/test_rope_dsv4.py \
  tests/ut/attention/test_dsa_v1.py tests/ut/attention/test_dsa_cp.py

Final-head repository CI:

  • CPU UT: 4190 passed, 38 existing skips, actual pytest summary inspected.
  • Pre-commit: all hooks passed; full production/example/test mypy checks passed for Python 3.10, 3.11 and 3.12.
  • Standalone Gitleaks also passed. The initial seven missing test dictionary annotations were fixed before the successful final-head checks.

Historical causal evidence, distinct from this branch's CI: an earlier isolated Ascend experiment with vLLM a97dacb7106ee49f39f3d1fc6ae1800ff724e01d reproduced third-position acceptance 0.4933095450490633, matching the failing CI's complete outputs. With fixed diagnostic admission conditions in the same directory, the ownership fix passed, reverting it restored the exact failure, and reapplying it passed with identical tokens/counts to the first fixed run. Two subsequent original uninstrumented main singletons passed; historical release attempts were resource-blocked. Immutable causal archive SHA256: da75a996eba465e79837c834a5b21d72e9f4b0ae811163593c5eb6ba1f4484f6. The same Triton disk-cache seed produced both passes and failures, so cache contents are not claimed as the root cause.

The separate local four-card attempt on this branch did not complete: main stopped before generation because ModelScope tried to write .mdl through a read-only model-cache link; release was blocked before pytest by external device occupancy. Writable private metadata was prepared without downloading/modifying weights, and no external processes were stopped. These local attempts are not counted as passes or precision failures; the completed current-version device results above come from CI.

Remaining merge gate: ci-gate requires a maintainer to add ready-precise or ready-all for the repository's broader selected/full suite. The targeted dual-version /e2e run does not automatically satisfy that label-controlled gate. No merge is requested by this task.

Keep global KV-update RoPE results separate from rank-local attention metadata while preserving fixed-capacity buffers and stable graph replay addresses. Add numerical, multi-group, capacity and address-stability regression coverage.

Signed-off-by: shenzhao <shenzhao9@huawei.com>

zhao-stack commented Sep 11, 2026 •

Copy link
Copy Markdown
Collaborator Author

/e2e tests/e2e/pull_request/four_card/context_parallel/test_deepseek_v4.py::test_deepseek_v4_dsa_pcp_dspark
[Bot]: e2e command triggered. See workflow run for details.
[Bot]: e2e command completed successfully.

@github-actions

Copy link
Copy Markdown
Contributor

👋 Hi! Thank you for contributing to the vLLM Ascend project. The following points will speed up your PR merge:‌‌

  • A PR should do only one thing, smaller PRs enable faster reviews.
  • Every PR should include unit tests and end-to-end tests ‌to ensure it works and is not broken by other future PRs.
  • Write the commit message by fulfilling the PR description to help reviewer and future developers understand.

If CI fails, you can run linting and testing checks locally according Contributing and Testing.


Tip

💡 Consider Linking a Related Issue or RFC

Your PR title contains the [BugFix] tag, indicating a bug fix or new feature.

Linking a related issue or RFC in the PR description is strongly encouraged — it gives reviewers helpful context and speeds up the review. You can use any of these keywords:

  • Fixes #<issue_number>
  • Closes #<issue_number>
  • Resolves #<issue_number>
  • Refs #<rfc_or_issue_number> (for RFCs)

🙏 Thanks for helping us keep the project well-organized!

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical bug where RoPE runtime buffers were being aliased between PCP's global KV-update metadata and rank-local attention metadata. By isolating these buffers through caller-owned persistent maps, the change ensures that global position values do not corrupt local attention metadata, thereby fixing issues with generated tokens and speculative acceptance in affected PCP + DSpark batches.

Highlights

  • RoPE Buffer Isolation: Introduced caller-owned persistent RoPE buffer maps for PCP global metadata builders to prevent runtime-buffer aliasing between global KV-update metadata and rank-local attention metadata.
  • Memory Management: Implemented lazy allocation of RoPE buffers at full registered capacity, ensuring stable addresses for graph replay while maintaining existing behavior for uncached and speculative paths.
  • Testing and Validation: Added comprehensive unit tests in tests/ut/ops/test_rope_dsv4.py and tests/ut/attention/test_dsa_v1.py to verify buffer separation and stability across interleaved cache-group builds.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@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

Suggested PR Title:

[Attention][Feature] Isolate RoPE runtime buffers for context parallel metadata builders to prevent aliasing

Suggested PR Summary:

### What this PR does / why we need it?
This pull request introduces isolated RoPE runtime buffers for Context Parallel (PCP) metadata builders to prevent aliasing between global KV updates and rank-local attention positions during the same step. By passing a `rope_runtime_buffer` to `get_cos_and_sin_dsa`, caller-owned buffers remain stable, which is essential for graph replay.

### Does this PR introduce _any_ user-facing change?
No.

### How was this patch tested?
New unit tests were added in `tests/ut/attention/test_dsa_v1.py` and `tests/ut/ops/test_rope_dsv4.py` to verify that global and local RoPE buffers are kept separate and survive interleaved cache groups.

Add concrete dictionary types required by the repository mypy checks. Production code and the original e2e test remain byte-identical to the fix commit.

Signed-off-by: shenzhao <shenzhao9@huawei.com>
@zhao-stack
zhao-stack marked this pull request as ready for review September 11, 2026 02:59
@zhao-stack zhao-stack closed this Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant