feat(ascend): port E2E-verified ascend support from 0.20.2 to v0.3.0-dev (vllm 0.24.0) - #387
Open
tengqm wants to merge 7 commits into
Open
feat(ascend): port E2E-verified ascend support from 0.20.2 to v0.3.0-dev (vllm 0.24.0)#387tengqm wants to merge 7 commits into
tengqm wants to merge 7 commits into
Conversation
<!--
Copyright 2026 FlagOS Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
Vendor
test cases:
Test | Model | Type | Result
-- | -- | -- | --
Text math (7×8) | 27B | Text | ✅ 56
Text knowledge (Capital of France) | 27B | Text | ✅ Paris
Text math (7×8) | 35B | Text | ✅ 56
Text knowledge (Capital of France) | 35B | Text | ✅ Paris
Image description | 27B | Image | ✅ Correct (black text, white
background)
Image OCR | 27B | Image | ✅ "Hello"
Image description | 35B | Image | ✅ Correct (black text, white
background)
Image OCR | 35B | Image | ✅ "Hello"
Concurrent text (8 requests) | 27B | Text | ✅ 8/8
Concurrent images (8 requests) | 27B | Image | ✅ 8/8
Concurrent mixed (8 requests) | 27B | Mixed | ✅ 8/8
Concurrent text (8 requests) | 35B | Text | ✅ 8/8
Concurrent images (8 requests) | 35B | Image | ✅ 8/8
Concurrent mixed (8 requests) | 35B | Mixed | ✅ 8/8
Prime numbers | 27B | Text | ✅ 2, 3, 5, 7, 11
Prime numbers | 35B | Text | ✅ 2, 3, 5, 7, 11
Code generation | 27B | Text | ✅ Valid Python
Code generation | 35B | Text | ✅ Valid Python
```
export VLLM_PLUGINS=fl
export VLLM_FL_PLATFORM=ascend
vllm serve /models/Qwen3.6-35B-A3B \
--host 0.0.0.0 \
--trust-remote-code \
--max-model-len 4096 \
--enforce-eager \
--served-model-name qwen \
--tensor-parallel-size 2 \
--gpu-memory-utilization 0.8 \
--no-enable-chunked-prefill \
--no-async-scheduling \
--no-enable-prefix-caching
```
vllm serve crashes during EngineCore startup on Ascend 910B4 with
"coreDim is invalid (value 0)". The crash originates from
register_buffer("_k_scale", torch.tensor(1.0)) during model construction:
torch.tensor(scalar) materializes the constant via aten::lift_fresh, which
FlagGems intercepts. Its copy kernel computes a zero grid for a scalar/tiny
tensor, and the NPU rejects the launch with coreDim=0.
Blacklist lift_fresh, lift_fresh_copy, and _to_copy so these trivial copies
fall back to torch_npu (lossless). Verified on ascend-cann9.0.0: serve reaches
"Application startup complete" and inference is coherent (Qwen3-4B, TP=1).
In vllm 0.24.0 the profile_result-derived computations (torch_peak_increase, kv cache budget, cudagraph estimate) live at function level after the with-block. 1326a33's 0.20.2 ascend port had them inside the else branch, so after cherry-pick the NPU branch fell through and raised NameError on profile_result, then would have overwritten the NPU kv-cache budget with the profiling-derived value. Move the whole block inside the else and default cudagraph_memory_estimate to 0 so the CUDA-graph warning tail stays safe on NPU.
flag_gems ascend pow kernels use `if (A or B or C)` which the triton_ascend 3.2.0 fork frontend rejects (UnsupportedLanguageConstruct: chained boolean operators), so the Triton path crashed compiling the RoPE _compute_inv_freq `10000.0 ** t` (pow.Scalar -> pow_scalar). config_filter() matches on the impl function name (item[1].__name__), so the previous `- pow` entry was ineffective. List the wrapper names (pow_tensor_tensor(_) / pow_tensor_scalar(_) / pow_scalar) instead; falling back to torch_npu.pow is exact. Verified E2E on hw25 (ascend-cann9.0.0, vllm 0.24.0): Triton path serve + inference with pristine flag_gems pow.py, no container patch needed. Co-Authored-By: Claude <noreply@anthropic.com>
…s None) flag_gems cumsum kernels call get_num_sms(device_idx) -> torch_device_fn.get_device_properties(idx).multi_processor_count when N > 16384. On torch_npu 2.9.0 / triton_ascend 3.2.0 (cann8.5.0) the properties object lacks multi_processor_count -> None * 4 -> TypeError, killing EngineCore on the first decode step (vllm topk_topp_sampler torch.cumsum(probs_sort, dim=-1, out=probs_sort), batch < 8 -> pytorch path). hw25 (cann9.0.0, torch_npu 2.10.0) has a working multi_processor_count, so this is cann8.5.0-specific. config_filter() matches on the impl function name (item[1].__name__), so both wrapper names are listed: "cumsum" (aten::cumsum) and "cumsum_out" (aten::cumsum.out). Falling back to torch_npu.cumsum is exact. E2E verified on hw26 (cann8.5.0) flagtree path: Qwen3-4B serve + knowledge/math requests, no cumsum/coreDim crash markers.
… stride crash torch_npu._npu_rotary_embedding (ATB path) calls repeat_interleave to expand the cos/sin cache for GQA. Under triton_ascend 3.2.0 (cann8.5.0) the flag_gems repeat_interleave pointwise copy kernel (rank-3, t2048) fails to compile: ConvertTritonIRToLinalgIR 'strides must not be zero' -> MLIRCompilationError. The exception raises inside the vendor.ascend rotary impl on the first decode call; CachedOp's silent failure path marks the impl failed (no warning log) and falls back to the flag_gems rope kernel, which hangs/garbage-collects the first request under the triton compiler. Blacklisting repeat_interleave falls back to the exact torch_npu native implementation. This PR was written in part with the assistance of generative AI.
flag_gems linear (aten::linear wrapper) kernel spins forever on device under triton_ascend 3.2.0 for decode (M=1) shapes: AICore pinned at ~111% while EngineCore stalls in get_current_stream (driver.py:219). Fresh SIGUSR1 stack dump of the hung EngineCore pinned the hang inside flag_gems/ops/linear.py:173 linear -> triton runner -> get_current_stream. flagtree 0.6.0+ascend3.2 compiles the same kernel fine. mm/addmm already blacklisted; linear was the remaining GEMM path in the hot decode loop. Falling back to torch_npu.linear is exact. E2E-verified on hw26 (cann8.5.0): Qwen3-4B both requests HTTP 200, coherent output, no crash. Co-Authored-By: Claude <noreply@anthropic.com>
Contributor
Author
Ascend CANN 8.5.0 (hw26) — dual-compiler E2E verification for vllm 0.24.0Verified 2026-08-18 on hw26 (ascend-cann8.5.0, 910B4, Qwen3-4B TP1). Both compiler paths pass E2E — serve reaches
Four blacklist commits added on this branch for the triton path under triton_ascend 3.2.0:
The container Environment: torch 2.9.0+cpu / torch_npu 2.9.0 / flag_gems 5.3.4 / vllm 0.24.0+flagos (cp311 aarch64 empty wheel). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports the E2E-verified Ascend (Huawei NPU) support from the vllm 0.20.2 baseline
(PR #307, commit
1326a33) ontov0.3.0-dev, which tracks vllm 0.24.0.The 0.24.0 upgrade (#274) never touched the ascend directory:
get_name()still returned
"ASCEND_FL", and 0.24.0'sAttentionBackendEnumhas no suchmember — extensions are only accepted via the
CUSTOMslot — so any Ascendlaunch failed with
ValueError: Unknown attention backend: 'ASCEND_FL'(
vllm/model_executor/layers/attention/attention.py:401). This PR restores aworking ascend backend on the 0.24.0 baseline.
Contents
1326a33, 13 files): the E2E-verified 0.20.2 ascendimplementation — CUSTOM-backend registration,
get_supported_kernel_block_sizes → [128],supports_update_block_table, NPU-aware platform config, FLA/GDNops, fused_moe kernels, MMEncoder attention.
baeafde): blacklistlift_fresh/lift_fresh_copy/_to_copyinascend.yaml(coreDim=0 crash on scalar tensor init);E2E-required, merged into this branch.
worker.py— see the dedicated section below.ascend.yaml): the Triton path crashedcompiling RoPE
_compute_inv_freq(pow.Scalar->pow_scalar) becauseflag_gems 5.3.4 ascend
pow.pyuses chainedorconditions thetriton_ascend 3.2.0 fork rejects.
- powwas ineffective (config_filtermatches function name, not op name); replaced with the wrapper names
pow_tensor_tensor(_)/pow_tensor_scalar(_)/pow_scalar. E2E-verifiedon a pristine pow.py — no container patch needed.
Test results (from PR #307, vllm 0.20.2 / ascend-cann9.0.0, Qwen3.6-27B-A3B and
Qwen3.6-35B-A3B, TP=2)
0.24.0 verification status
PASSED (2026-08-17, hw25, ascend-cann9.0.0, Qwen3-4B TP1, flagtree
path). Serve reached
Application startup complete; OpManager dispatchedrms_norm/rotary_embeddingtovendor.ascendandsilu_and_multodefault.flagos. Both smoke inferences returned coherent output(knowledge → "Paris...", math → "56..."), system fingerprint
vllm-0.24.0-563743c8.PASSED (2026-08-18, hw25, same stack, Triton path). With
compiler triton(PYTHONPATH=/opt/triton → triton 3.2.0 fork + triton_ascend 3.2.1 overlay),
serve reached
Application startup completeand both smoke inferencesreturned coherent output (knowledge → "Paris...", math → "56..."), same
fingerprint
vllm-0.24.0-563743c8. The pow-compile blocker is fixed in thisPR via the function-name blacklist (item 4): flag_gems 5.3.4 ascend
pow.pyuses chained
orconditions the triton_ascend 3.2.0 fork rejects(
UnsupportedLanguageConstruct: chained boolean operators) while compilingRoPE
_compute_inv_freq(pow.Scalar->pow_scalar). The previous- powentry was ineffective because
config_filter()matches the impl functionname; the wrapper names now route pow to
torch_npu(exact), verified E2E ona pristine pow.py — no container patch needed. Upstream pow.py fix still
tracked in the 0.24.0 report (遗留 items).
Runtime stack: image
flagos-runtime-ascend-cann9.0.0:2.1.2, Python 3.11.15,torch 2.10.0+cpu, torch_npu 2.10.0, flag_gems 5.3.4, flagtree
0.6.1+ascend3.5 (default), side
/opt/triton= triton_ascend 3.2.1(covers triton as 3.2.0 fork), vllm 0.24.0+flagos, device Ascend910B4-1, CANN 9.0.0.
Non-fatal: flag_gems 5.3.4
index_select.py:45logical and/or deprecationwarning (upstream, no correctness impact; tracked in the 0.24.0 report
遗留事项). GDN/hybrid models (Qwen3-Next) remain unsupported on 0.24.0
(see "0.24.0-specific customizations" §2).
0.24.0-specific customizations
Recorded here because the 0.24.0 baseline requires changes beyond a verbatim
port, per the porting plan.
worker.py— NPU profile branch restructure. In 0.24.0 theprofile_result-derived computations (torch_peak_increase, kv-cache budget,cudagraph estimate) live at function level after the profiling
with-block;in 0.20.2 they sat inside the
elsebranch. After cherry-pick the NPU branchfell through into the profiling block (NameError on
profile_result, then itwould have overwritten the NPU kv-cache budget with the profiling-derived
value). Fixed by moving the whole block inside
elseand defaultingcudagraph_memory_estimate = 0so the CUDA-graph warning tail stays safe onNPU.
0.24.0 restructured
vllm/model_executor/layers/mamba/gdn_linear_attn.pyinto a
mamba/gdn/package;qwen3_next.pylost itscausal_conv1d_fn/causal_conv1d_update/chunk_gated_delta_rulesymbols;and
fla/ops/__init__.pyno longer re-exportschunk_gated_delta_rule_fwd.The GDN patches in
patch.pytarget these symbols and silently no-op(try/except-wrapped). Plain-attention models (Qwen3, Qwen2, Llama, ...) are
unaffected. Restructuring the GDN patches for the new layout is follow-up
work.
ascend.yamllicense header. The v0.3.0-dev base file lacks the Apacheheader that
upstream/maincarries — a merge-time cosmetic delta, notintroduced by this PR.
Merge-back notes
vllm_fl/dispatch/backends/vendor/ascend/are byte-identicalto
upstream/main, so the eventual v0.3.0-dev → main merge will beconflict-free for the whole ascend directory.
git diff --checkreports two pre-existing "blank line at EOF" warnings inpatch.py/mm_encoder_attention.py, inherited verbatim from upgrade vllm to 0.20.2 on ascend platform #307(byte-identical to
upstream/main); intentionally left untouched.This PR was written in part with the assistance of generative AI.