Skip to content

[Spec Decode][MRV2] Support DFlash2 in MRV2 - #14594

Open
YzTongNiar wants to merge 5 commits into
vllm-project:mainfrom
YzTongNiar:dflash2
Open

YzTongNiar wants to merge 5 commits into
vllm-project:mainfrom
YzTongNiar:dflash2

Conversation

@YzTongNiar

@YzTongNiar YzTongNiar commented Aug 20, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does / why we need it?

Main Idea is supporting DFlash2 (https://inco.ai/blog/dflash2/) in MRV2

Currently, only eager mode is supported

Refering:
vllm-project/vllm#52816
vllm-project/vllm#52883

Includes:

  • dflash2 support in Qwen3.8-27B in MRV2
  • triton-ascend adaptation of the selector walk kernel
  • a bugfix for Qwen3.8 spec decode in MRV2

Does this PR introduce any user-facing change?

No change, accoridng to vllm upsteram PR, use the same argument:
--speculative-config '{"method": "dflash", "model": "draft_model_path"

How was this patch tested?

Currently, only Qwen3.8-27B-DFlash2 is available in MRV2, thus the PR is tested under this model

Use this script for testing in A3:

export VLLM_USE_V2_MODEL_RUNNER=1
export ASCEND_RT_VISIBLE_DEVICES=0,1,2,3
export HCCL_OP_EXPANSION_MODE="AIV"
export OMP_PROC_BIND=false
export OMP_NUM_THREADS=1
export HCCL_BUFFSIZE=512
export HCCL_CONNECT_TIMEOUT=3600
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export VLLM_ASCEND_BALANCE_SCHEDULING=0

vllm serve /mnt/share/weight/Qwen3.8-27B \
    --host 0.0.0.0 \
    --port 8100 \
    --tensor-parallel-size 4 \
    --served-model-name qwen3.8 \
    --max-num-seqs 4 \
    --max-model-len 4096 \
    --max-num-batched-tokens 8192 \
    --trust-remote-code \
    --limit-mm-per-prompt '{"image": 0, "video": 0}' \
    --gpu-memory-utilization 0.85 \
    --enforce-eager \
    --speculative-config '{"method": "dflash", "model": "/mnt/share/t00886357/weights/Qwen3.8-27B-DFlash2", "num_speculative_tokens": 7, "enforce_eager": true}' \

vllm v0.27.1
Need Upstream Cherry-Pick:
vllm-project/vllm#52816
vllm-project/vllm#52883

vllm-ascend: main @ 1ba7ee4

Route DFlash2DraftModel architectures to AscendDFlash2Speculator on the
V2 model runner. The speculator combines the upstream DFlash2Speculator
(candidate top-k, low-rank bilinear selector scoring, sequential path
walk, and draft-logits scatter for lossless rejection sampling) with the
Ascend NPU wiring inherited from AscendDFlashSpeculator (attention
metadata building, aclgraph manager, set_attn bookkeeping, propose
wrapper) via cooperative MRO.

Includes:
- triton-ascend adaptation of the selector walk kernel: tldevice.log1p
  has no NPU lowering and is replaced by the algebraically equivalent
  tl.log(1 - u); philox (tl.rand) requires int32 seed/offset operands,
  so the int64 seeds, sample positions and candidate ids are cast (an
  int64 counter lowers to 64-bit multiplies whose runtime helper
  __multi3 is unavailable); fp64 gumbel noise is unavailable on NPU.
  The kernel is swapped onto the upstream dflash2 speculator module
  from patch_v2/patch_dflash_speculator.
- guard the seq_lens update in the shared autoregressive speculator for
  hybrid linear-attention layers (GDN metadata has no seq_lens), which
  otherwise crashes the first propose on Qwen3.5/Qwen3.8-family models.

Verified on 910B: both kernels match torch references (greedy walk
elementwise, T=1 sampling path executes, full-vocab scatter including
duplicate-token overwrite and -inf reset); e2e reaches the draft loop.

Signed-off-by: YzTongNiar <1667927948@qq.com>
@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 introduces support for DFlash2 speculative decoding within the MRV2 framework for Ascend NPUs. It includes the necessary infrastructure to integrate DFlash2, provides a specialized Triton kernel to handle NPU-specific constraints, and addresses a sequence length tracking issue that affected hybrid attention models.

Highlights

  • DFlash2 Support: Added support for DFlash2 speculative decoding in MRV2, including the implementation of an Ascend-compatible selector walk kernel.
  • Triton-Ascend Adaptation: Implemented a custom Triton kernel to bypass limitations in tldevice.log1p support by using an algebraically equivalent log(1 - u) variant.
  • Bug Fix: Resolved a bug in autoregressive sequence length updates for hybrid models that mix full-attention and linear-attention layers.
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. ↩

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

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

[Ops][Feature] Add AscendDFlash2Speculator and support for DFlash2 draft models

Suggested PR Summary:

### What this PR does / why we need it?
This PR introduces support for the `DFlash2` speculative drafter on Ascend NPUs by implementing `AscendDFlash2Speculator`. It includes:
- Swapping in an Ascend-compatible Triton kernel (`_selector_walk_kernel_ascend`) that replaces `tldevice.log1p` with an algebraically equivalent `log(1 - u)` transform and uses `int32` operands for Philox compatibility.
- Adding initialization logic for `AscendDFlash2Speculator` when `DFlash2DraftModel` is present in the architectures.
- Adding a safety check in `_ascend_update_seq_lens` to skip sequence length updates for hybrid models that do not track `seq_lens`.

Feedback:
A review comment suggests improving the safety of the `hasattr(attn_meta, "seq_lens")` check by using `getattr(attn_meta, "seq_lens", None) is None` to handle cases where `attn_meta` or `seq_lens` itself is `None`.

### Does this PR introduce _any_ user-facing change?
No, this is an internal enablement of DFlash2 speculative decoding on Ascend NPUs.

### How was this patch tested?
CI tests and verification on Ascend hardware.

Comment on lines +393 to +394
if not hasattr(attn_meta, "seq_lens"):
continue

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.

high

Using hasattr(attn_meta, "seq_lens") is not fully safe if attn_meta is None or if the seq_lens attribute itself is None. To prevent potential TypeError or AttributeError crashes in hybrid models or edge cases, it is safer to use getattr(attn_meta, "seq_lens", None) is None to handle both cases defensively.

Suggested change
if not hasattr(attn_meta, "seq_lens"):
continue
if getattr(attn_meta, "seq_lens", None) is None:
continue

Upstream model states (MambaHybridAttnMetadata) pass is_prefilling per
request through get_extra_common_attn_kwargs, while build_attn_metadata
also passed it explicitly, so the first real request on any hybrid
attention model crashes with: AscendCommonAttentionMetadata() got
multiple values for keyword argument 'is_prefilling'.

Merge via setdefault: the model-specific tensor wins when present, the
explicit parameter remains the fallback.

Verified: Qwen3.8-27B (48 linear-attention + 16 full-attention layers)
serves correctly on MRV2 after the fix.

Signed-off-by: YzTongNiar <1667927948@qq.com>
With the target on FULL_DECODE_ONLY, the draft previously went through
dynamo twice and crashed: torch_npu fake tensors mis-derive the
selector's hidden[:, :, None] broadcast (mul((s, 7, 16, 256), (s, 256,
1)) fails dimension checks), and the spec-level enforce_eager is not
consulted by the V2 speculator graph decision.

- AscendDFlash2Speculator.init_cudagraph_manager: honor
  speculative_config.enforce_eager by forcing CUDAGraphMode.NONE for the
  draft speculator while the target keeps its own cudagraph mode.
- patch_qwen3_dflash: mark the DFlash/DFlash2 draft model classes and
  CandidateSelector with _ignore_compile_vllm so the draft skips
  torch.compile entirely (submodule decorators compile independently of
  the outer model, so each decorated class must be flagged).

Verified on 910B: target FULL_DECODE_ONLY + eager DFlash2 draft serves
greedy output identical to full-eager, acceptance length ~5.6 @
num_speculative_tokens=7.

Signed-off-by: YzTongNiar <1667927948@qq.com>
Signed-off-by: lilinsiman <lilinsiman@gmail.com>
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

# Hybrid models mix full-attention layers with linear-attention
# ones (e.g. GDN), whose metadata tracks recurrent state via
# indices and has no seq_lens to bump.
if not hasattr(attn_meta, "seq_lens"):

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.

This is not the root cause.
#14791 solved it.

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.

ur right, wait to delete this after #14791 is merged.

@lilinsiman lilinsiman added the ready-precise run selected e2e test for pr label Aug 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

)

DFlashQwen3Model._ignore_compile_vllm = True
DFlash2Qwen3Model._ignore_compile_vllm = True

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.

open a new patch file named patch_qwen3_dflash2

Comment thread vllm_ascend/worker/v2/attn_utils.py Outdated
)
# Upstream model states (e.g. MambaHybrid) pass is_prefilling per
# request through the extra kwargs; merge instead of passing it twice.
common_attn_metadata_extra_kwargs.setdefault("is_prefilling", is_prefilling)

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.

pr #14649 solved

Signed-off-by: lilinsiman <lilinsiman@gmail.com>
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-conflicts ready-precise run selected e2e test for pr

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants