0
tests/e2e/pull_request/four_card/_310p/__init__.py
Normal file
0
tests/e2e/pull_request/four_card/_310p/__init__.py
Normal file
@@ -0,0 +1,49 @@
|
||||
#
|
||||
# Copyright (c) 2026 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
# This file is a part of the vllm-ascend project.
|
||||
|
||||
from tests.e2e.conftest import VllmRunner
|
||||
|
||||
|
||||
def test_qwen3_dense_tp2_fp16():
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
]
|
||||
max_tokens = 5
|
||||
with VllmRunner(
|
||||
"Qwen/Qwen3-8B",
|
||||
tensor_parallel_size=2,
|
||||
enforce_eager=True,
|
||||
dtype="float16",
|
||||
max_model_len=16384,
|
||||
) as vllm_model:
|
||||
vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
|
||||
|
||||
def test_qwen3_dense_tp4_w8a8():
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
]
|
||||
max_tokens = 5
|
||||
with VllmRunner(
|
||||
"vllm-ascend/Qwen3-32B-W8A8",
|
||||
tensor_parallel_size=4,
|
||||
enforce_eager=True,
|
||||
dtype="float16",
|
||||
quantization="ascend",
|
||||
max_model_len=16384,
|
||||
) as vllm_model:
|
||||
vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
@@ -0,0 +1,74 @@
|
||||
#
|
||||
# Copyright (c) 2026 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
# This file is a part of the vllm-ascend project.
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.e2e.conftest import VllmRunner
|
||||
|
||||
|
||||
def test_qwen3_moe_tp4_fp16():
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
]
|
||||
max_tokens = 5
|
||||
with VllmRunner(
|
||||
"Qwen/Qwen3-30B-A3B",
|
||||
tensor_parallel_size=4,
|
||||
enforce_eager=True,
|
||||
dtype="float16",
|
||||
max_model_len=16384,
|
||||
max_num_batched_tokens=2048,
|
||||
max_num_seqs=256,
|
||||
) as vllm_model:
|
||||
vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
|
||||
|
||||
def test_qwen3_moe_tp2_w8a8():
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
]
|
||||
max_tokens = 5
|
||||
with VllmRunner(
|
||||
"vllm-ascend/Qwen3-30B-A3B-W8A8",
|
||||
tensor_parallel_size=2,
|
||||
enforce_eager=True,
|
||||
dtype="float16",
|
||||
quantization="ascend",
|
||||
max_model_len=16384,
|
||||
max_num_batched_tokens=2048,
|
||||
max_num_seqs=256,
|
||||
) as vllm_model:
|
||||
vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
|
||||
|
||||
@pytest.mark.skip("Probabilistic failure, need fix")
|
||||
def test_qwen3_5_moe_tp4_fp16():
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
]
|
||||
max_tokens = 5
|
||||
with VllmRunner(
|
||||
"Qwen/Qwen3.5-35B-A3B",
|
||||
tensor_parallel_size=4,
|
||||
enforce_eager=True,
|
||||
dtype="float16",
|
||||
max_model_len=16384,
|
||||
max_num_batched_tokens=2048,
|
||||
max_num_seqs=256,
|
||||
) as vllm_model:
|
||||
vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
34
tests/e2e/pull_request/four_card/_310p/test_vl_model_310p.py
Normal file
34
tests/e2e/pull_request/four_card/_310p/test_vl_model_310p.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Copyright (c) 2026 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
# This file is a part of the vllm-ascend project.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from tests.e2e.conftest import wait_until_npu_memory_free
|
||||
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
full_dir = os.path.dirname(os.path.dirname(current_dir))
|
||||
sys.path.insert(0, full_dir)
|
||||
|
||||
# ruff: noqa: E402
|
||||
from tests.e2e.pull_request.utils_310p import run_vl_model_test
|
||||
|
||||
|
||||
@wait_until_npu_memory_free(target_free_percentage=0.7)
|
||||
def test_qwen3_vl_8b_tp2_fp16():
|
||||
"""Qwen3-VL-8B dual-card FP16 test"""
|
||||
run_vl_model_test(model_name="Qwen/Qwen3-VL-8B-Instruct", tensor_parallel_size=2, max_tokens=5)
|
||||
0
tests/e2e/pull_request/four_card/__init__.py
Normal file
0
tests/e2e/pull_request/four_card/__init__.py
Normal file
@@ -0,0 +1,493 @@
|
||||
#
|
||||
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
"""PCP/DCP long-sequence accuracy guards.
|
||||
|
||||
Run `pytest tests/e2e/pull_request/four_card/context_parallel/test_accuracy.py`.
|
||||
"""
|
||||
|
||||
import os
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, cast
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from PIL import Image
|
||||
from vllm import SamplingParams
|
||||
|
||||
from tests.e2e.conftest import DPVllmRunner, VllmRunner, wait_until_npu_memory_free
|
||||
|
||||
DEEPSEEK_V2_LITE = "vllm-ascend/DeepSeek-V2-Lite-W8A8"
|
||||
DEEPSEEK_MTP = "wemaster/deepseek_mtp_main_random_bf16"
|
||||
MAX_NUM_SEQS = 4
|
||||
E2E_ROOT = Path(__file__).resolve().parents[3]
|
||||
QWEN_IMAGE_PATH = E2E_ROOT / "prompts" / "qwen.png"
|
||||
|
||||
FULL_DECODE_GRAPH = {
|
||||
"cudagraph_mode": "FULL_DECODE_ONLY",
|
||||
"cudagraph_capture_sizes": [MAX_NUM_SEQS],
|
||||
}
|
||||
|
||||
|
||||
COMMON_PROMPTS = [
|
||||
"The capital of France is",
|
||||
"Hello, my name is Tom, I am",
|
||||
"The president of United States is",
|
||||
]
|
||||
|
||||
DSV2_PROMPTS = [
|
||||
"The president of the United States is",
|
||||
"The capital of France is",
|
||||
]
|
||||
|
||||
DSV2_PCP_GOLDEN = [
|
||||
"The president of the United States is a man who is not only a liar, but",
|
||||
"The capital of France is Paris.\nThe currency of France is the Euro",
|
||||
]
|
||||
|
||||
DSV2_DCP_GOLDEN = [
|
||||
"The president of the United States is a man who is not only a liar, but",
|
||||
"The capital of France is Paris.\nThe currency of France is the Euro",
|
||||
]
|
||||
|
||||
QWEN3_GOLDEN = [
|
||||
"The capital of France is Paris. The capital of",
|
||||
"Hello, my name is Tom, I am 12 years old",
|
||||
"The president of United States is the head of state and",
|
||||
]
|
||||
|
||||
QWEN3_NEXT_GOLDEN = [
|
||||
"The capital of France is Paris. The capital of",
|
||||
"Hello, my name is Tom, I am 12 years old",
|
||||
"The president of United States is the head of state and",
|
||||
]
|
||||
|
||||
DSV3_2_GOLDEN = [
|
||||
"The capital of France isbearerhaloce梗",
|
||||
"Hello, my name is Tom, I am" + "ERIC slicpacelike Chop",
|
||||
"The president of United States is平行astra unbehetroni",
|
||||
]
|
||||
|
||||
DSV3_2_GOLDEN_BACKUPS = (
|
||||
[
|
||||
"The capital of France isbearerhaloce梗",
|
||||
"Hello, my name is Tom, I am" + "ERIC slicpacelike Chop",
|
||||
"The president of United States isoint054 Rund959arki",
|
||||
],
|
||||
[
|
||||
"The capital of France isbearerdenomorthal",
|
||||
"Hello, my name is Tom, I am" + "ERIC slicpacelike Chop",
|
||||
"The president of United States is平行astra unbehetroni",
|
||||
],
|
||||
[
|
||||
"The capital of France isbearerdenomorthal",
|
||||
"Hello, my name is Tom, I am" + "ERIC slicpacelike Chop",
|
||||
"The president of United States isoint054 Rund959arki",
|
||||
],
|
||||
)
|
||||
|
||||
DSV3_2_DCP_GOLDEN = [
|
||||
"The capital of France isoint054 Rund959arki",
|
||||
"Hello, my name is Tom, I am" + "ERIC slicpacelike挂",
|
||||
"The president of United States isoint054 Rund959arki",
|
||||
]
|
||||
|
||||
DEEPSEEK_MTP3_GOLDEN = [
|
||||
"The capital of France is Salmonella团团 elsewhereッγκ",
|
||||
"Hello, my name is Tom, I amEiSlowukt Analysis sprouts",
|
||||
"The president of United States is Salmonella团团 elsewhereッγκ",
|
||||
]
|
||||
|
||||
DEEPSEEK_V4_PROMPTS = [
|
||||
"Hello, my name is",
|
||||
"What is the meaning of life?",
|
||||
]
|
||||
|
||||
DEEPSEEK_V4_GOLDEN = ["Hello, my name is {name} and I", 'What is the meaning of life?",\n "What is']
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AccuracyCase:
|
||||
name: str
|
||||
model: str
|
||||
prompts: Sequence[str]
|
||||
expected_outputs: Sequence[str] | Sequence[Sequence[str]]
|
||||
max_tokens: int
|
||||
runner_kwargs: dict[str, Any]
|
||||
|
||||
|
||||
def _run_accuracy_case(case: AccuracyCase) -> None:
|
||||
runner_cls = DPVllmRunner if case.runner_kwargs.get("data_parallel_size", 1) > 1 else VllmRunner
|
||||
with runner_cls(case.model, **case.runner_kwargs) as runner:
|
||||
outputs = runner.generate_greedy(list(case.prompts), case.max_tokens)
|
||||
|
||||
if isinstance(case.expected_outputs[0], str):
|
||||
expected_outputs = cast(Sequence[str], case.expected_outputs)
|
||||
match_outputs_with_goldens(outputs, expected_outputs)
|
||||
else:
|
||||
# If multiple expected output sets are provided, the output is considered correct if it matches any of the sets.
|
||||
multi_expected_outputs = cast(Sequence[Sequence[str]], case.expected_outputs)
|
||||
tries = []
|
||||
for expected in multi_expected_outputs:
|
||||
try:
|
||||
match_outputs_with_goldens(outputs, expected)
|
||||
except AssertionError as exc:
|
||||
tries.append(f"Output did not match expected set:\n{exc}")
|
||||
else:
|
||||
break
|
||||
if len(tries) == len(multi_expected_outputs):
|
||||
failure_details = "\n\n".join(tries)
|
||||
raise AssertionError(f"Output did not match any of the expected output sets:\n{failure_details}")
|
||||
|
||||
|
||||
def match_outputs_with_goldens(outputs: list[tuple[list[int], str]], goldens: Sequence[str]) -> None:
|
||||
"""Helper function to compare output with golden output, ignoring whitespace differences."""
|
||||
outputs_str: Sequence[str] = [output[1] for output in outputs]
|
||||
assert len(outputs_str) == len(goldens)
|
||||
for index, (output, golden) in enumerate(zip(outputs_str, goldens)):
|
||||
assert isinstance(output, str) and isinstance(golden, str), "Both output and golden must be strings"
|
||||
assert output and golden, "Output and golden should not be empty"
|
||||
assert output.strip() == golden.strip()
|
||||
|
||||
|
||||
@patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"HCCL_BUFFSIZE": "768",
|
||||
"VLLM_WORKER_MULTIPROC_METHOD": "spawn",
|
||||
"OMP_NUM_THREADS": "1",
|
||||
"OMP_PROC_BIND": "false",
|
||||
"PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
|
||||
},
|
||||
)
|
||||
@wait_until_npu_memory_free(target_free_percentage=0.8)
|
||||
def test_qwen3_vl_multimodal_pcp_accuracy_guard() -> None:
|
||||
image = Image.open(QWEN_IMAGE_PATH).convert("RGB")
|
||||
single_image_prompt = (
|
||||
"<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n"
|
||||
"<|im_start|>user\n"
|
||||
"<|vision_start|><|image_pad|><|vision_end|>"
|
||||
"Describe this image in detail.<|im_end|>\n"
|
||||
"<|im_start|>assistant\n"
|
||||
)
|
||||
multi_image_prompt = (
|
||||
"<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n"
|
||||
"<|im_start|>user\n"
|
||||
"<|vision_start|><|image_pad|><|vision_end|>"
|
||||
"<|vision_start|><|image_pad|><|vision_end|>"
|
||||
"Compare these two images and describe similarities.<|im_end|>\n"
|
||||
"<|im_start|>assistant\n"
|
||||
)
|
||||
inputs = [
|
||||
{
|
||||
"prompt": single_image_prompt,
|
||||
"multi_modal_data": {"image": image},
|
||||
},
|
||||
{
|
||||
"prompt": multi_image_prompt,
|
||||
"multi_modal_data": {"image": [image, image.copy()]},
|
||||
},
|
||||
]
|
||||
sampling_params = SamplingParams(max_tokens=16, temperature=0.0)
|
||||
|
||||
with VllmRunner(
|
||||
"Qwen/Qwen3-VL-8B-Instruct",
|
||||
enforce_eager=False,
|
||||
max_model_len=4096,
|
||||
tensor_parallel_size=2,
|
||||
prefill_context_parallel_size=2,
|
||||
decode_context_parallel_size=1,
|
||||
max_num_batched_tokens=1024,
|
||||
block_size=128,
|
||||
limit_mm_per_prompt={"image": 2},
|
||||
mm_processor_kwargs={
|
||||
"min_pixels": 28 * 28,
|
||||
"max_pixels": 1280 * 28 * 28,
|
||||
"fps": 1,
|
||||
},
|
||||
compilation_config={
|
||||
"cudagraph_mode": "FULL_DECODE_ONLY",
|
||||
"cudagraph_capture_sizes": [MAX_NUM_SEQS],
|
||||
},
|
||||
) as runner:
|
||||
outputs = runner.model.generate(inputs, sampling_params=sampling_params)
|
||||
|
||||
assert len(outputs) == len(inputs)
|
||||
for output in outputs:
|
||||
assert output.outputs and output.outputs[0].text.strip()
|
||||
|
||||
|
||||
DSV2_COMMON_KWARGS: dict[str, Any] = {
|
||||
"max_model_len": 1024,
|
||||
"max_num_seqs": MAX_NUM_SEQS,
|
||||
"max_num_batched_tokens": 1024,
|
||||
"enable_expert_parallel": True,
|
||||
"enable_chunked_prefill": True,
|
||||
"enable_prefix_caching": True,
|
||||
"block_size": 128,
|
||||
"quantization": "ascend",
|
||||
"compilation_config": FULL_DECODE_GRAPH,
|
||||
"additional_config": {"enable_flashcomm1": True},
|
||||
}
|
||||
|
||||
DSV2_PARALLEL_CASES = [
|
||||
AccuracyCase(
|
||||
name="dsv2_pcp_dcp_full_features",
|
||||
model=DEEPSEEK_V2_LITE,
|
||||
prompts=DSV2_PROMPTS,
|
||||
expected_outputs=DSV2_PCP_GOLDEN,
|
||||
max_tokens=10,
|
||||
runner_kwargs={
|
||||
**DSV2_COMMON_KWARGS,
|
||||
"tensor_parallel_size": 2,
|
||||
"prefill_context_parallel_size": 2,
|
||||
"decode_context_parallel_size": 2,
|
||||
"cp_kv_cache_interleave_size": 128,
|
||||
"long_prefill_token_threshold": 4,
|
||||
},
|
||||
),
|
||||
AccuracyCase(
|
||||
name="dsv2_pcp_only_full_features",
|
||||
model=DEEPSEEK_V2_LITE,
|
||||
prompts=DSV2_PROMPTS,
|
||||
expected_outputs=DSV2_PCP_GOLDEN,
|
||||
max_tokens=10,
|
||||
runner_kwargs={
|
||||
**DSV2_COMMON_KWARGS,
|
||||
"tensor_parallel_size": 2,
|
||||
"prefill_context_parallel_size": 2,
|
||||
"decode_context_parallel_size": 1,
|
||||
"cp_kv_cache_interleave_size": 128,
|
||||
"long_prefill_token_threshold": 4,
|
||||
},
|
||||
),
|
||||
AccuracyCase(
|
||||
name="dsv2_dcp_only_full_features",
|
||||
model=DEEPSEEK_V2_LITE,
|
||||
prompts=DSV2_PROMPTS,
|
||||
expected_outputs=DSV2_DCP_GOLDEN,
|
||||
max_tokens=10,
|
||||
runner_kwargs={
|
||||
**DSV2_COMMON_KWARGS,
|
||||
"tensor_parallel_size": 2,
|
||||
"prefill_context_parallel_size": 1,
|
||||
"decode_context_parallel_size": 2,
|
||||
"long_prefill_token_threshold": 64,
|
||||
"compilation_config": {
|
||||
**FULL_DECODE_GRAPH,
|
||||
"pass_config": {"enable_sp": True},
|
||||
},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
FULL_FEATURE_MODEL_CASES = [
|
||||
AccuracyCase(
|
||||
name="qwen3_pcp_dcp_full_features",
|
||||
model="vllm-ascend/Qwen3-30B-A3B-W8A8",
|
||||
prompts=COMMON_PROMPTS,
|
||||
expected_outputs=QWEN3_GOLDEN,
|
||||
max_tokens=5,
|
||||
runner_kwargs={
|
||||
"max_model_len": 1024,
|
||||
"max_num_seqs": MAX_NUM_SEQS,
|
||||
"max_num_batched_tokens": 1024,
|
||||
"tensor_parallel_size": 2,
|
||||
"prefill_context_parallel_size": 2,
|
||||
"decode_context_parallel_size": 1,
|
||||
"enable_expert_parallel": True,
|
||||
"enable_chunked_prefill": True,
|
||||
"enable_prefix_caching": True,
|
||||
"block_size": 128,
|
||||
"quantization": "ascend",
|
||||
"long_prefill_token_threshold": 4,
|
||||
"compilation_config": FULL_DECODE_GRAPH,
|
||||
"additional_config": {"enable_flashcomm1": True},
|
||||
},
|
||||
),
|
||||
AccuracyCase(
|
||||
name="qwen3_next_pcp_dcp_full_features",
|
||||
model="Qwen/Qwen3-Next-80B-A3B-Instruct",
|
||||
prompts=COMMON_PROMPTS,
|
||||
expected_outputs=QWEN3_NEXT_GOLDEN,
|
||||
max_tokens=5,
|
||||
runner_kwargs={
|
||||
"enforce_eager": True,
|
||||
"max_model_len": 1024,
|
||||
"tensor_parallel_size": 2,
|
||||
"prefill_context_parallel_size": 2,
|
||||
"decode_context_parallel_size": 1,
|
||||
"max_num_batched_tokens": 1024,
|
||||
"enable_expert_parallel": True,
|
||||
# TODO(qcs): We should set `long_prefill_token_threshold` to 4
|
||||
# when chunked prefill with PCP is stable.
|
||||
"long_prefill_token_threshold": 128,
|
||||
"gpu_memory_utilization": 0.8,
|
||||
"block_size": 128,
|
||||
# FlashComm1 is disabled for qwen3_next until the PCP decode path is fixed.
|
||||
"additional_config": {"enable_flashcomm1": False},
|
||||
},
|
||||
),
|
||||
AccuracyCase(
|
||||
name="dsv3_2_pcp_dcp_full_features",
|
||||
model="vllm-ascend/DeepSeek-V3.2-W8A8-Pruning",
|
||||
prompts=COMMON_PROMPTS,
|
||||
# TODO(qcs): Remove multi-expected_outputs after the first request output is stable.
|
||||
expected_outputs=(DSV3_2_GOLDEN, *DSV3_2_GOLDEN_BACKUPS),
|
||||
max_tokens=5,
|
||||
runner_kwargs={
|
||||
"max_model_len": 1024,
|
||||
"max_num_seqs": MAX_NUM_SEQS,
|
||||
"max_num_batched_tokens": 1024,
|
||||
"tensor_parallel_size": 2,
|
||||
"prefill_context_parallel_size": 2,
|
||||
"decode_context_parallel_size": 2,
|
||||
"enable_expert_parallel": True,
|
||||
"enable_chunked_prefill": True,
|
||||
"enable_prefix_caching": True,
|
||||
"gpu_memory_utilization": 0.2,
|
||||
"cp_kv_cache_interleave_size": 128,
|
||||
"block_size": 128,
|
||||
"quantization": "ascend",
|
||||
# TODO(qcs): We should set `long_prefill_token_threshold` to 4
|
||||
# when chunked prefill with PCP is stable.
|
||||
"long_prefill_token_threshold": 128,
|
||||
"compilation_config": FULL_DECODE_GRAPH,
|
||||
"additional_config": {"enable_flashcomm1": True},
|
||||
# graph_mode is disabled for dsv32 until the PCP gatherv3 out of index issue is fixed.
|
||||
"enforce_eager": True,
|
||||
},
|
||||
),
|
||||
AccuracyCase(
|
||||
name="dsv3_2_sfa_dcp_replicated_indexer",
|
||||
model="vllm-ascend/DeepSeek-V3.2-W8A8-Pruning",
|
||||
prompts=COMMON_PROMPTS,
|
||||
expected_outputs=DSV3_2_DCP_GOLDEN,
|
||||
max_tokens=5,
|
||||
runner_kwargs={
|
||||
"max_model_len": 1024,
|
||||
"max_num_seqs": MAX_NUM_SEQS,
|
||||
"max_num_batched_tokens": 1024,
|
||||
"data_parallel_size": 2,
|
||||
"tensor_parallel_size": 2,
|
||||
"prefill_context_parallel_size": 1,
|
||||
"decode_context_parallel_size": 2,
|
||||
"enable_expert_parallel": True,
|
||||
"enable_chunked_prefill": True,
|
||||
"enable_prefix_caching": True,
|
||||
"gpu_memory_utilization": 0.4,
|
||||
"cp_kv_cache_interleave_size": 1,
|
||||
"block_size": 128,
|
||||
"quantization": "ascend",
|
||||
"long_prefill_token_threshold": 128,
|
||||
"compilation_config": FULL_DECODE_GRAPH,
|
||||
"additional_config": {
|
||||
"enable_flashcomm1": True,
|
||||
"enable_dsa_cp": True,
|
||||
"enable_sparse_sfa_c8": True,
|
||||
"enable_sparse_li_c8": True,
|
||||
},
|
||||
"speculative_config": {
|
||||
"method": "mtp",
|
||||
"num_speculative_tokens": 3,
|
||||
},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
AccuracyCase(
|
||||
name="deepseek_mtp3_pcp_dcp_full_features",
|
||||
model=DEEPSEEK_MTP,
|
||||
prompts=COMMON_PROMPTS,
|
||||
expected_outputs=DEEPSEEK_MTP3_GOLDEN,
|
||||
max_tokens=5,
|
||||
runner_kwargs={
|
||||
"max_model_len": 1024,
|
||||
"max_num_seqs": MAX_NUM_SEQS,
|
||||
"max_num_batched_tokens": 1024,
|
||||
"tensor_parallel_size": 2,
|
||||
"prefill_context_parallel_size": 2,
|
||||
"decode_context_parallel_size": 2,
|
||||
"enable_expert_parallel": True,
|
||||
"enable_chunked_prefill": True,
|
||||
"enable_prefix_caching": True,
|
||||
"block_size": 128,
|
||||
"long_prefill_token_threshold": 4,
|
||||
"speculative_config": {
|
||||
"method": "mtp",
|
||||
"num_speculative_tokens": 3,
|
||||
},
|
||||
"compilation_config": FULL_DECODE_GRAPH,
|
||||
"additional_config": {"enable_flashcomm1": True},
|
||||
},
|
||||
),
|
||||
marks=pytest.mark.skip(reason="Temporarily skip MTP with PCP/DCP until the token layout issue is fixed."),
|
||||
),
|
||||
AccuracyCase(
|
||||
name="deepseek_v4_w4a8_dsa_cp_full_features",
|
||||
model="gdydems/DeepSeek-V4-Flash-w4a8-mtp",
|
||||
prompts=DEEPSEEK_V4_PROMPTS,
|
||||
expected_outputs=DEEPSEEK_V4_GOLDEN,
|
||||
max_tokens=5,
|
||||
runner_kwargs={
|
||||
"max_model_len": 8192,
|
||||
"max_num_seqs": 16,
|
||||
"max_num_batched_tokens": 4096,
|
||||
"dtype": "auto",
|
||||
"tensor_parallel_size": 4,
|
||||
"prefill_context_parallel_size": 1,
|
||||
"decode_context_parallel_size": 1,
|
||||
"enable_expert_parallel": True,
|
||||
"gpu_memory_utilization": 0.9,
|
||||
"quantization": "ascend",
|
||||
"tokenizer_mode": "deepseek_v4",
|
||||
"block_size": 128,
|
||||
"compilation_config": {
|
||||
"cudagraph_mode": "FULL_DECODE_ONLY",
|
||||
},
|
||||
"additional_config": {
|
||||
"enable_flashcomm1": True,
|
||||
"enable_dsa_cp": True,
|
||||
},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"HCCL_BUFFSIZE": "768",
|
||||
},
|
||||
)
|
||||
@wait_until_npu_memory_free(target_free_percentage=0.8)
|
||||
@pytest.mark.parametrize("case", DSV2_PARALLEL_CASES, ids=lambda case: case.name)
|
||||
def test_dsv2_lite_parallel_config_accuracy(case: AccuracyCase) -> None:
|
||||
_run_accuracy_case(case)
|
||||
|
||||
|
||||
@patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"HCCL_BUFFSIZE": "768",
|
||||
"PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
|
||||
},
|
||||
)
|
||||
@wait_until_npu_memory_free(target_free_percentage=0.8)
|
||||
@pytest.mark.parametrize("case", FULL_FEATURE_MODEL_CASES, ids=lambda case: case.name)
|
||||
def test_models_pcp_dcp_full_feature_accuracy(case: AccuracyCase) -> None:
|
||||
_run_accuracy_case(case)
|
||||
@@ -0,0 +1,76 @@
|
||||
#
|
||||
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
# This file is a part of the vllm-ascend project.
|
||||
#
|
||||
"""DeepSeek-V2-Lite prefix-cache CP guard.
|
||||
|
||||
Run `pytest tests/e2e/pull_request/four_card/context_parallel/test_prefix_caching_cp.py`.
|
||||
"""
|
||||
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
from tests.e2e.conftest import VllmRunner
|
||||
|
||||
# TODO(qcs): We should use Qwen3.5 for this test when it's fixed and available,
|
||||
# so that we can test the hybrid kv cache with both full attention and linear attention.
|
||||
MODEL = "vllm-ascend/DeepSeek-V2-Lite-W8A8"
|
||||
MAX_NUM_SEQS = 2
|
||||
THREAD_ENV = {
|
||||
"OMP_NUM_THREADS": "1",
|
||||
"MKL_NUM_THREADS": "1",
|
||||
"OPENBLAS_NUM_THREADS": "1",
|
||||
"NUMEXPR_NUM_THREADS": "1",
|
||||
}
|
||||
|
||||
DSV2_LITE_PREFIX_PROMPT = (
|
||||
"You are reading a compact synthetic operations ledger. "
|
||||
"Use only the rows below when answering the final question.\n"
|
||||
+ "\n".join(
|
||||
f"Row {i}: route R{i:03d} moves cargo from zone {i % 11} to zone {(i * 7) % 13}; priority is {i % 5}."
|
||||
for i in range(64)
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
INPUT_PROMPTS = [
|
||||
DSV2_LITE_PREFIX_PROMPT + "Question: What route is listed in row 17? Answer briefly.",
|
||||
DSV2_LITE_PREFIX_PROMPT + "Question: What priority is listed in row 42? Answer briefly.",
|
||||
]
|
||||
|
||||
|
||||
@patch.dict(os.environ, THREAD_ENV)
|
||||
def test_dsv2_lite_prefix_cache_with_pcp() -> None:
|
||||
with VllmRunner(
|
||||
MODEL,
|
||||
block_size=128,
|
||||
max_model_len=2048,
|
||||
max_num_seqs=MAX_NUM_SEQS,
|
||||
max_num_batched_tokens=2048,
|
||||
tensor_parallel_size=2,
|
||||
prefill_context_parallel_size=2,
|
||||
decode_context_parallel_size=1,
|
||||
enforce_eager=True,
|
||||
enable_expert_parallel=True,
|
||||
enable_prefix_caching=True,
|
||||
quantization="ascend",
|
||||
) as vllm_model:
|
||||
prefix_cache_outputs = vllm_model.generate_greedy(INPUT_PROMPTS, 8)
|
||||
|
||||
assert len(prefix_cache_outputs) == len(INPUT_PROMPTS)
|
||||
for output_ids, output_text in prefix_cache_outputs:
|
||||
assert output_ids
|
||||
assert output_text
|
||||
@@ -0,0 +1,85 @@
|
||||
#
|
||||
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
# This file is a part of the vllm-ascend project.
|
||||
# Adapted from vllm/tests/basic_correctness/test_basic_correctness.py
|
||||
#
|
||||
"""Compare the short outputs of HF and vLLM when using greedy sampling.
|
||||
|
||||
Run `pytest tests/e2e/pull_request/four_card/spec_decode/test_mtp_qwen3_next.py`.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from vllm.config import CompilationConfig
|
||||
from vllm.v1.metrics.reader import Counter, Vector
|
||||
|
||||
from tests.e2e.conftest import VllmRunner, cleanup_dist_env_and_memory
|
||||
|
||||
os.environ["VLLM_WORKER_MULTIPROC_METHOD"] = "spawn"
|
||||
|
||||
MODELS = ["Qwen/Qwen3-Next-80B-A3B-Instruct"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model_name", MODELS)
|
||||
def test_qwen3_next_mtp_acceptance_tp4(model_name):
|
||||
golden = [0.85, 0.46, 0.19]
|
||||
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
"The president of the United States is",
|
||||
"The capital of France is",
|
||||
"The future of AI is",
|
||||
]
|
||||
|
||||
max_tokens = 1024
|
||||
|
||||
with VllmRunner(
|
||||
model_name,
|
||||
tensor_parallel_size=4,
|
||||
max_model_len=4096,
|
||||
gpu_memory_utilization=0.8,
|
||||
distributed_executor_backend="mp",
|
||||
disable_log_stats=False,
|
||||
speculative_config={
|
||||
"method": "qwen3_next_mtp",
|
||||
"num_speculative_tokens": 3,
|
||||
},
|
||||
compilation_config=CompilationConfig(cudagraph_mode="FULL_DECODE_ONLY", cudagraph_capture_sizes=[20]),
|
||||
) as spec_vllm_model:
|
||||
_ = spec_vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
metrics = spec_vllm_model.model.get_metrics()
|
||||
|
||||
num_drafts = 0
|
||||
num_accepted_tokens_per_pos = [0] * 3
|
||||
for metric in metrics:
|
||||
if metric.name == "vllm:spec_decode_num_drafts":
|
||||
assert isinstance(metric, Counter)
|
||||
num_drafts += metric.value
|
||||
elif metric.name == "vllm:spec_decode_num_accepted_tokens_per_pos":
|
||||
assert isinstance(metric, Vector)
|
||||
for pos in range(len(metric.values)):
|
||||
num_accepted_tokens_per_pos[pos] += metric.values[pos]
|
||||
|
||||
acceptance_per_pos = [num_accepted_tokens / num_drafts for num_accepted_tokens in num_accepted_tokens_per_pos]
|
||||
|
||||
match = all((a >= b) or (b - a < 0.06) for a, b in zip(acceptance_per_pos, golden))
|
||||
if not match:
|
||||
print(f"acceptance_per_pos: {acceptance_per_pos}")
|
||||
print(f"golden: {golden}")
|
||||
|
||||
assert match
|
||||
cleanup_dist_env_and_memory()
|
||||
49
tests/e2e/pull_request/four_card/test_data_parallel_tp2.py
Normal file
49
tests/e2e/pull_request/four_card/test_data_parallel_tp2.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.e2e.conftest import wait_until_npu_memory_free
|
||||
|
||||
MODELS = ["Qwen/Qwen3-30B-A3B"]
|
||||
REPO_ROOT = Path(__file__).resolve().parents[4]
|
||||
DATA_PARALLEL_SCRIPT = REPO_ROOT / "examples" / "offline_data_parallel.py"
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="broken, fix me")
|
||||
@pytest.mark.parametrize("model", MODELS)
|
||||
@pytest.mark.parametrize("max_tokens", [32])
|
||||
@patch.dict(os.environ, {"ASCEND_RT_VISIBLE_DEVICES": "0,1,2,3"})
|
||||
@wait_until_npu_memory_free(target_free_percentage=0.7)
|
||||
def test_qwen3_inference_dp2_tp2(model, max_tokens):
|
||||
env = os.environ.copy()
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
str(DATA_PARALLEL_SCRIPT),
|
||||
"--model",
|
||||
model,
|
||||
"--dp-size",
|
||||
"2",
|
||||
"--tp-size",
|
||||
"2",
|
||||
"--node-size",
|
||||
"1",
|
||||
"--node-rank",
|
||||
"0",
|
||||
"--trust-remote-code",
|
||||
]
|
||||
|
||||
print(f"Running subprocess: {' '.join(cmd)}")
|
||||
proc = subprocess.run(cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=600)
|
||||
output = proc.stdout.decode(errors="ignore")
|
||||
|
||||
print(output)
|
||||
|
||||
assert "DP rank 0 needs to process" in output
|
||||
assert "DP rank 1 needs to process" in output
|
||||
assert "Generated text:" in output
|
||||
assert proc.returncode == 0
|
||||
@@ -0,0 +1,164 @@
|
||||
#
|
||||
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
# This file is a part of the vllm-ascend project.
|
||||
#
|
||||
|
||||
import json
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
import requests
|
||||
from vllm.utils.network_utils import get_open_port
|
||||
|
||||
from tests.e2e.conftest import DisaggPDProxy, RemotePDServer, VllmRunner, wait_until_npu_memory_free
|
||||
|
||||
|
||||
@patch.dict(os.environ, {"VLLM_ASCEND_ENABLE_FLASHCOMM1": "1"})
|
||||
@wait_until_npu_memory_free()
|
||||
def test_moe_w8a8_tp_pp_ep_full_decode_only():
|
||||
"""Verify W8A8 MoE generation with TP, PP, EP, and full decode only."""
|
||||
model = "vllm-ascend/DeepSeek-V3.2-W8A8-Pruning"
|
||||
prompts = ["Hello, my name is"]
|
||||
|
||||
with VllmRunner(
|
||||
model,
|
||||
enable_expert_parallel=True,
|
||||
quantization="ascend",
|
||||
max_model_len=1024,
|
||||
tensor_parallel_size=2,
|
||||
pipeline_parallel_size=2,
|
||||
gpu_memory_utilization=0.8,
|
||||
compilation_config={"cudagraph_capture_sizes": [2, 4, 6, 8, 10, 12], "cudagraph_mode": "FULL_DECODE_ONLY"},
|
||||
) as vllm_model:
|
||||
outputs = vllm_model.generate_greedy(prompts, max_tokens=500)
|
||||
|
||||
assert len(outputs) == len(prompts)
|
||||
assert len(outputs[0][1]) > len(prompts[0])
|
||||
|
||||
|
||||
@wait_until_npu_memory_free()
|
||||
def test_pd_disaggregation_w8a8_sfa_dsa_full_decode_only():
|
||||
"""Verify W8A8 1P1D PD disaggregation with full decode only."""
|
||||
prefiller_port = [get_open_port()]
|
||||
decoder_port = [get_open_port()]
|
||||
proxy_port = get_open_port()
|
||||
|
||||
ld_library_path = os.environ.get("LD_LIBRARY_PATH", "")
|
||||
env_dict = {
|
||||
"LD_LIBRARY_PATH": f"/usr/local/lib:{ld_library_path}",
|
||||
}
|
||||
|
||||
vllm_server_args = [
|
||||
[
|
||||
"--port",
|
||||
str(prefiller_port[0]),
|
||||
"--model",
|
||||
"vllm-ascend/DeepSeek-V3.2-W8A8-Pruning",
|
||||
"--trust-remote-code",
|
||||
"--enable-request-id-headers",
|
||||
"--no-enable-prefix-caching",
|
||||
"--enable-expert-parallel",
|
||||
"--quantization",
|
||||
"ascend",
|
||||
"--max-model-len",
|
||||
"1024",
|
||||
"--max-num-batched-tokens",
|
||||
"1024",
|
||||
"--max-num-seqs",
|
||||
"4",
|
||||
"--tensor-parallel-size",
|
||||
"2",
|
||||
"--gpu-memory-utilization",
|
||||
"0.9",
|
||||
"--kv-transfer-config",
|
||||
json.dumps(
|
||||
{
|
||||
"kv_connector": "MooncakeConnectorV1",
|
||||
"kv_role": "kv_producer",
|
||||
"kv_port": "30000",
|
||||
"kv_connector_extra_config": {
|
||||
"prefill": {"dp_size": 1, "tp_size": 2},
|
||||
"decode": {"dp_size": 1, "tp_size": 2},
|
||||
},
|
||||
}
|
||||
),
|
||||
"--enforce-eager",
|
||||
],
|
||||
[
|
||||
"--port",
|
||||
str(decoder_port[0]),
|
||||
"--model",
|
||||
"vllm-ascend/DeepSeek-V3.2-W8A8-Pruning",
|
||||
"--trust-remote-code",
|
||||
"--enable-request-id-headers",
|
||||
"--no-enable-prefix-caching",
|
||||
"--enable-expert-parallel",
|
||||
"--quantization",
|
||||
"ascend",
|
||||
"--max-model-len",
|
||||
"1024",
|
||||
"--max-num-batched-tokens",
|
||||
"1024",
|
||||
"--max-num-seqs",
|
||||
"4",
|
||||
"--tensor-parallel-size",
|
||||
"2",
|
||||
"--gpu-memory-utilization",
|
||||
"0.9",
|
||||
"--kv-transfer-config",
|
||||
json.dumps(
|
||||
{
|
||||
"kv_connector": "MooncakeConnectorV1",
|
||||
"kv_role": "kv_consumer",
|
||||
"kv_port": "30200",
|
||||
"kv_connector_extra_config": {
|
||||
"prefill": {"dp_size": 1, "tp_size": 2},
|
||||
"decode": {"dp_size": 1, "tp_size": 2},
|
||||
},
|
||||
}
|
||||
),
|
||||
"--compilation-config",
|
||||
json.dumps(
|
||||
{
|
||||
"cudagraph_mode": "FULL_DECODE_ONLY",
|
||||
"cudagraph_capture_sizes": [1, 2, 4, 8],
|
||||
}
|
||||
),
|
||||
],
|
||||
]
|
||||
|
||||
with (
|
||||
RemotePDServer(vllm_server_args, env_dict=env_dict),
|
||||
DisaggPDProxy(
|
||||
port=proxy_port,
|
||||
prefiller_ports=prefiller_port,
|
||||
decoder_ports=decoder_port,
|
||||
) as proxy,
|
||||
):
|
||||
response = requests.post(
|
||||
proxy.url_for("v1", "completions"),
|
||||
json={
|
||||
"model": "vllm-ascend/DeepSeek-V3.2-W8A8-Pruning",
|
||||
"prompt": "Hello, my name is",
|
||||
"max_tokens": 5,
|
||||
"temperature": 0.0,
|
||||
},
|
||||
timeout=600,
|
||||
)
|
||||
response.raise_for_status()
|
||||
output = response.json()
|
||||
|
||||
assert output["choices"][0]["text"]
|
||||
117
tests/e2e/pull_request/four_card/test_deepseek_v4.py
Normal file
117
tests/e2e/pull_request/four_card/test_deepseek_v4.py
Normal file
@@ -0,0 +1,117 @@
|
||||
#
|
||||
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
# This file is a part of the vllm-ascend project.
|
||||
# Adapted from vllm/tests/basic_correctness/test_basic_correctness.py
|
||||
#
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
from tests.e2e.conftest import VllmRunner, wait_until_npu_memory_free
|
||||
|
||||
os.environ["PYTORCH_NPU_ALLOC_CONF"] = "expandable_segments:True"
|
||||
os.environ["VLLM_WORKER_MULTIPROC_METHOD"] = "spawn"
|
||||
|
||||
|
||||
@patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"VLLM_ASCEND_ENABLE_FLASHCOMM1": "1",
|
||||
},
|
||||
)
|
||||
@wait_until_npu_memory_free()
|
||||
def test_deepseek_v4_w4a8_tp4_basic_greedy():
|
||||
"""Verify DeepSeek V4 W4A8 basic greedy generation with TP4 and EP."""
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
"What is the meaning of life?",
|
||||
]
|
||||
max_tokens = 5
|
||||
|
||||
with VllmRunner(
|
||||
"gdydems/DeepSeek-V4-Flash-w4a8-mtp",
|
||||
max_model_len=8192,
|
||||
max_num_seqs=16,
|
||||
max_num_batched_tokens=4096,
|
||||
dtype="auto",
|
||||
tensor_parallel_size=4,
|
||||
enable_expert_parallel=True,
|
||||
gpu_memory_utilization=0.9,
|
||||
quantization="ascend",
|
||||
tokenizer_mode="deepseek_v4",
|
||||
block_size=128,
|
||||
compilation_config={
|
||||
"cudagraph_mode": "FULL_DECODE_ONLY",
|
||||
},
|
||||
speculative_config={"num_speculative_tokens": 1, "method": "mtp"},
|
||||
) as vllm_model:
|
||||
outputs = vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
expected_token_ids = [
|
||||
[19923, 14, 1026, 2329, 344, 680, 2852, 95, 305, 342],
|
||||
[3085, 344, 270, 5281, 294, 1988, 33, 3955, 361, 582, 3085, 344],
|
||||
]
|
||||
assert len(outputs) == len(example_prompts)
|
||||
for i, (output_ids, output_str) in enumerate(outputs):
|
||||
assert len(output_str) > 0
|
||||
assert len(output_ids) > 0
|
||||
assert output_ids == expected_token_ids[i]
|
||||
|
||||
|
||||
@patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"VLLM_ASCEND_ENABLE_FLASHCOMM1": "1",
|
||||
},
|
||||
)
|
||||
@wait_until_npu_memory_free()
|
||||
def test_deepseek_v4_w4a8_tp4_index_cache_freq4():
|
||||
"""IndexCache freq=4 must produce non-empty greedy outputs identical in
|
||||
shape to the baseline test, verifying skip_topk/topk_indices_buffer
|
||||
plumbing (DSAModules → AscendDSAImpl) is wired correctly across both
|
||||
serial and dual-stream paths.
|
||||
"""
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
"The capital of France is",
|
||||
"What is the meaning of life?",
|
||||
]
|
||||
max_tokens = 5
|
||||
|
||||
with VllmRunner(
|
||||
"gdydems/DeepSeek-V4-Flash-w4a8-mtp",
|
||||
max_model_len=8192,
|
||||
max_num_seqs=16,
|
||||
max_num_batched_tokens=4096,
|
||||
dtype="auto",
|
||||
tensor_parallel_size=4,
|
||||
enable_expert_parallel=True,
|
||||
gpu_memory_utilization=0.9,
|
||||
quantization="ascend",
|
||||
tokenizer_mode="deepseek_v4",
|
||||
block_size=128,
|
||||
compilation_config={
|
||||
"cudagraph_mode": "FULL_DECODE_ONLY",
|
||||
},
|
||||
hf_overrides={
|
||||
"use_index_cache": True,
|
||||
"index_topk_freq": 4,
|
||||
},
|
||||
) as vllm_model:
|
||||
outputs = vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
|
||||
assert len(outputs) == len(example_prompts)
|
||||
for output_ids, output_str in outputs:
|
||||
assert len(output_str) > 0
|
||||
assert len(output_ids) > 0
|
||||
673
tests/e2e/pull_request/four_card/test_graph_mode.py
Normal file
673
tests/e2e/pull_request/four_card/test_graph_mode.py
Normal file
@@ -0,0 +1,673 @@
|
||||
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import contextlib
|
||||
import multiprocessing
|
||||
import os
|
||||
import queue
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
from vllm import LLM, SamplingParams
|
||||
from vllm.utils.network_utils import get_open_port
|
||||
|
||||
from tests.e2e.conftest import cleanup_dist_env_and_memory, wait_until_npu_memory_free
|
||||
from tests.e2e.pull_request.utils import PROMPTS_LONG, PROMPTS_SHORT
|
||||
|
||||
QWEN3 = "Qwen/Qwen3-0.6B"
|
||||
DEEPSEEK_V2_LITE = "vllm-ascend/DeepSeek-V2-Lite-W8A8"
|
||||
|
||||
QWEN3_PROMPTS_SHORT_BASELINE = [
|
||||
{
|
||||
"token_ids": [444, 2210, 13, 358, 2776, 264, 220, 17, 17, 4666, 6284, 5458, 504, 5616, 13, 358],
|
||||
"logprobs": [
|
||||
-4.082355976104736,
|
||||
-1.8239692449569702,
|
||||
-0.7117747664451599,
|
||||
-0.19997872412204742,
|
||||
-0.9261042475700378,
|
||||
-1.156473994255066,
|
||||
-1.6240551471710205,
|
||||
-0.4462895393371582,
|
||||
-1.7280645370483398,
|
||||
-0.03872159123420715,
|
||||
-0.006691192742437124,
|
||||
-1.729214072227478,
|
||||
-1.4082962274551392,
|
||||
-1.2914694547653198,
|
||||
-0.30865585803985596,
|
||||
-0.19352123141288757,
|
||||
],
|
||||
},
|
||||
{
|
||||
"token_ids": [279, 1852, 438, 279, 4767, 315, 279, 3639, 19140, 13, 1096, 374, 1576, 279, 4767, 315],
|
||||
"logprobs": [
|
||||
-1.329056978225708,
|
||||
-1.9254618883132935,
|
||||
-0.16430772840976715,
|
||||
-0.07656584680080414,
|
||||
-0.20014727115631104,
|
||||
-0.009215084835886955,
|
||||
-0.6130291223526001,
|
||||
-0.7763887047767639,
|
||||
-0.7629368901252747,
|
||||
-0.7878186702728271,
|
||||
-1.5039337873458862,
|
||||
-0.34936416149139404,
|
||||
-1.1888322830200195,
|
||||
-0.7042690515518188,
|
||||
-0.7195678949356079,
|
||||
-0.5817108154296875,
|
||||
],
|
||||
},
|
||||
{
|
||||
"token_ids": [12095, 13, 576, 6722, 315, 15344, 374, 21718, 13, 576, 6722, 315, 17689, 374, 24081, 13],
|
||||
"logprobs": [
|
||||
-0.4164676070213318,
|
||||
-0.650071918964386,
|
||||
-1.2497835159301758,
|
||||
-0.41373029351234436,
|
||||
-0.10615493357181549,
|
||||
-1.7584394216537476,
|
||||
-0.014708652161061764,
|
||||
-0.09583187848329544,
|
||||
-0.05152897536754608,
|
||||
-0.5075417757034302,
|
||||
-0.04731765389442444,
|
||||
-0.004497650545090437,
|
||||
-0.734763503074646,
|
||||
-0.006638852413743734,
|
||||
-0.03197822347283363,
|
||||
-0.02921150252223015,
|
||||
],
|
||||
},
|
||||
{
|
||||
"token_ids": [537, 1101, 264, 29016, 8645, 714, 264, 27155, 17991, 315, 1246, 582, 3887, 11, 975, 11],
|
||||
"logprobs": [
|
||||
-2.253798246383667,
|
||||
-0.6618977785110474,
|
||||
-1.1329351663589478,
|
||||
-1.5547151565551758,
|
||||
-2.502274751663208,
|
||||
-0.6954820156097412,
|
||||
-0.16636334359645844,
|
||||
-0.9172338247299194,
|
||||
-0.8822890520095825,
|
||||
-0.6346368789672852,
|
||||
-1.1293692588806152,
|
||||
-0.355191707611084,
|
||||
-0.23319871723651886,
|
||||
-0.3448694050312042,
|
||||
-0.015147067606449127,
|
||||
-0.023382289335131645,
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
QWEN3_PROMPTS_LONG_BASELINE = [
|
||||
{
|
||||
"token_ids": [4710, 1249, 11625, 419, 3491, 11, 582, 1184, 311, 990, 279, 7506, 315, 328, 1543, 323],
|
||||
"logprobs": [
|
||||
-1.1807091236114502,
|
||||
-0.7548902034759521,
|
||||
-0.06596270948648453,
|
||||
-0.21140751242637634,
|
||||
-0.17375628650188446,
|
||||
-0.012642711400985718,
|
||||
-0.8002182841300964,
|
||||
-0.5415360331535339,
|
||||
-0.005715575534850359,
|
||||
-1.0228453874588013,
|
||||
-0.49259668588638306,
|
||||
-0.5897566080093384,
|
||||
-0.0001705739414319396,
|
||||
-0.008742272853851318,
|
||||
-0.0008293526479974389,
|
||||
-0.20622815191745758,
|
||||
],
|
||||
},
|
||||
{
|
||||
"token_ids": [4710, 1249, 11625, 419, 3491, 11, 582, 646, 990, 279, 2701, 5486, 25, 6771, 400, 47],
|
||||
"logprobs": [
|
||||
-0.6663188934326172,
|
||||
-0.31078171730041504,
|
||||
-0.1298152357339859,
|
||||
-0.17452840507030487,
|
||||
-0.14723201096057892,
|
||||
-0.011572140268981457,
|
||||
-0.5467657446861267,
|
||||
-1.1380974054336548,
|
||||
-0.8147172927856445,
|
||||
-0.2714247703552246,
|
||||
-1.2143880128860474,
|
||||
-0.84015291929245,
|
||||
-0.7829729318618774,
|
||||
-1.5653221607208252,
|
||||
-0.631846010684967,
|
||||
-1.7602534294128418,
|
||||
],
|
||||
},
|
||||
{
|
||||
"token_ids": [4710, 1249, 11625, 419, 3491, 11, 582, 646, 990, 279, 2701, 5486, 25, 6771, 400, 1124],
|
||||
"logprobs": [
|
||||
-0.979120671749115,
|
||||
-0.74333256483078,
|
||||
-0.22203724086284637,
|
||||
-0.31942218542099,
|
||||
-0.2570137679576874,
|
||||
-0.012989195063710213,
|
||||
-0.939915657043457,
|
||||
-1.038073182106018,
|
||||
-0.4173763394355774,
|
||||
-0.17641115188598633,
|
||||
-1.119675874710083,
|
||||
-1.2035267353057861,
|
||||
-0.7646281719207764,
|
||||
-1.5680660009384155,
|
||||
-0.5041356086730957,
|
||||
-1.0269478559494019,
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
DEEPSEEK_V2_LITE_PROMPTS_SHORT_BASELINE = [
|
||||
{
|
||||
"token_ids": [185, 40, 608, 245, 207, 17, 15, 1012, 1712, 12608, 11, 285, 304, 463, 803, 14079],
|
||||
"logprobs": [
|
||||
-4.621323108673096,
|
||||
-3.077335834503174,
|
||||
-1.3346226215362549,
|
||||
-0.9667544364929199,
|
||||
-2.628643274307251,
|
||||
-1.0353751182556152,
|
||||
-1.9108309745788574,
|
||||
-0.9746326208114624,
|
||||
-0.07258293032646179,
|
||||
-2.375293016433716,
|
||||
-1.7306381464004517,
|
||||
-1.7152436971664429,
|
||||
-0.5231161713600159,
|
||||
-1.2002876996994019,
|
||||
-0.945522665977478,
|
||||
-2.263141393661499,
|
||||
],
|
||||
},
|
||||
{
|
||||
"token_ids": [245, 668, 779, 317, 441, 889, 245, 69524, 11, 548, 245, 42357, 11, 245, 39925, 11],
|
||||
"logprobs": [
|
||||
-1.9601335525512695,
|
||||
-2.9202699661254883,
|
||||
-0.935673713684082,
|
||||
-1.9938912391662598,
|
||||
-2.5300979614257812,
|
||||
-2.1406760215759277,
|
||||
-2.01202130317688,
|
||||
-2.9169065952301025,
|
||||
-0.5751016736030579,
|
||||
-0.5648833513259888,
|
||||
-1.2851604223251343,
|
||||
-2.817136764526367,
|
||||
-0.826416015625,
|
||||
-0.8048403263092041,
|
||||
-1.2173839807510376,
|
||||
-0.32657456398010254,
|
||||
],
|
||||
},
|
||||
{
|
||||
"token_ids": [8913, 13, 185, 549, 19305, 280, 7239, 317, 254, 28071, 13, 185, 549, 13829, 13451, 279],
|
||||
"logprobs": [
|
||||
-0.39279282093048096,
|
||||
-0.8086707592010498,
|
||||
-0.7125738859176636,
|
||||
-1.6490240097045898,
|
||||
-1.9644602537155151,
|
||||
-0.4904576539993286,
|
||||
-0.0832064300775528,
|
||||
-0.00726190535351634,
|
||||
-0.38277116417884827,
|
||||
-0.2940319776535034,
|
||||
-0.7399694323539734,
|
||||
-0.07564151287078857,
|
||||
-0.7905924320220947,
|
||||
-1.8350766897201538,
|
||||
-0.4076140522956848,
|
||||
-0.20184002816677094,
|
||||
],
|
||||
},
|
||||
{
|
||||
"token_ids": [6464, 11, 285, 359, 487, 82, 889, 1872, 276, 752, 34993, 13, 1733, 20838, 11106, 276],
|
||||
"logprobs": [
|
||||
-2.4984869956970215,
|
||||
-0.7851194739341736,
|
||||
-0.6003906726837158,
|
||||
-1.4917504787445068,
|
||||
-0.9962607026100159,
|
||||
-0.004179196432232857,
|
||||
-1.5068310499191284,
|
||||
-0.4656505584716797,
|
||||
-0.0027805021964013577,
|
||||
-0.11925199627876282,
|
||||
-0.35579147934913635,
|
||||
-0.35492807626724243,
|
||||
-1.605405330657959,
|
||||
-1.38535737991333,
|
||||
-0.930091142654419,
|
||||
-0.004122450482100248,
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
DEEPSEEK_V2_LITE_PROMPTS_LONG_BASELINE = [
|
||||
{
|
||||
"token_ids": [185, 185, 1679, 26430, 279, 16145, 285, 8204, 185, 185, 13483, 9890, 16982, 457, 17693, 829],
|
||||
"logprobs": [
|
||||
-0.24134111404418945,
|
||||
-0.06949862092733383,
|
||||
-2.196256637573242,
|
||||
-0.3235849142074585,
|
||||
-5.745722592109814e-05,
|
||||
-7.152554530875932e-07,
|
||||
-9.381330892210826e-05,
|
||||
-3.4570632578834193e-06,
|
||||
-0.0006878394051454961,
|
||||
-6.079655122448457e-06,
|
||||
-7.152554530875932e-07,
|
||||
-8.356221951544285e-05,
|
||||
-1.8358061424805783e-05,
|
||||
-1.2636104656849056e-05,
|
||||
-1.5497195136049413e-06,
|
||||
-2.0146166207268834e-05,
|
||||
],
|
||||
},
|
||||
{
|
||||
"token_ids": [185, 185, 1679, 26430, 279, 16145, 285, 8204, 185, 185, 13483, 9890, 16982, 457, 17693, 829],
|
||||
"logprobs": [
|
||||
-0.27419501543045044,
|
||||
-0.08238636702299118,
|
||||
-2.0612075328826904,
|
||||
-1.0054221153259277,
|
||||
-4.947062916471623e-05,
|
||||
-4.768370445162873e-07,
|
||||
-2.1457441107486375e-05,
|
||||
-3.933898824470816e-06,
|
||||
-0.00018630675913300365,
|
||||
-1.1920920996999484e-06,
|
||||
-4.172316494077677e-06,
|
||||
-0.00010048838157672435,
|
||||
-1.0847986231965479e-05,
|
||||
-1.1920928244535389e-07,
|
||||
-1.1920920996999484e-06,
|
||||
-1.4185804502631072e-05,
|
||||
],
|
||||
},
|
||||
{
|
||||
"token_ids": [185, 185, 1679, 26430, 279, 16145, 285, 8204, 185, 185, 13483, 9890, 16982, 457, 17693, 829],
|
||||
"logprobs": [
|
||||
-0.14973750710487366,
|
||||
-0.09690935909748077,
|
||||
-1.8772021532058716,
|
||||
-1.6962311267852783,
|
||||
-0.0001429217227268964,
|
||||
-8.344646289515367e-07,
|
||||
-7.617183291586116e-05,
|
||||
-3.814689989667386e-06,
|
||||
-0.0010556369088590145,
|
||||
-6.318072337307967e-06,
|
||||
-1.1920920996999484e-06,
|
||||
-0.00035553809721022844,
|
||||
-2.729855441430118e-05,
|
||||
-2.3841855067985307e-07,
|
||||
-1.4305104514278355e-06,
|
||||
-4.410733708937187e-06,
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
CASE_QWEN_ACLGRAPH = {
|
||||
"model": QWEN3,
|
||||
"quantization": None,
|
||||
"prompts": {"short": PROMPTS_SHORT, "long": PROMPTS_LONG},
|
||||
"compilation_config": {"max_cudagraph_capture_size": 24, "cudagraph_mode": "FULL"},
|
||||
"tensor_parallel_size": 4,
|
||||
"data_parallel_size": 1,
|
||||
"enable_expert_parallel": False,
|
||||
"golden_answers": {"short": QWEN3_PROMPTS_SHORT_BASELINE, "long": QWEN3_PROMPTS_LONG_BASELINE},
|
||||
# TODO: it increases after profile graph memory is disabled, invetigate later
|
||||
"baseline_capture_mem": 0.30,
|
||||
"capture_mem_tolerance": 1.3,
|
||||
}
|
||||
|
||||
CASE_DS_ACLGRAPH = {
|
||||
"model": DEEPSEEK_V2_LITE,
|
||||
"quantization": "ascend",
|
||||
"prompts": {"short": PROMPTS_SHORT, "long": PROMPTS_LONG},
|
||||
"compilation_config": {"max_cudagraph_capture_size": 24, "cudagraph_mode": "FULL_AND_PIECEWISE"},
|
||||
"tensor_parallel_size": 2,
|
||||
"data_parallel_size": 2,
|
||||
"enable_expert_parallel": True,
|
||||
"golden_answers": {
|
||||
"short": DEEPSEEK_V2_LITE_PROMPTS_SHORT_BASELINE,
|
||||
"long": DEEPSEEK_V2_LITE_PROMPTS_LONG_BASELINE,
|
||||
},
|
||||
"baseline_capture_mem": 0.68,
|
||||
"capture_mem_tolerance": 1.5,
|
||||
}
|
||||
|
||||
CASE_DS_ACLGRAPH_ENPU = {
|
||||
**CASE_DS_ACLGRAPH,
|
||||
"env_vars": {"ENPU_ENABLE": "true"},
|
||||
}
|
||||
|
||||
# inherit from tests/e2e/pull_request/utils.py::compare_logprobs
|
||||
ATOL = 0.0689
|
||||
|
||||
_SAMPLING_PARAMS = SamplingParams(
|
||||
max_tokens=16,
|
||||
temperature=0.0,
|
||||
top_p=1.0,
|
||||
top_k=0,
|
||||
logprobs=20,
|
||||
)
|
||||
|
||||
|
||||
def _install_spies(metrics: dict[str, Any] | None):
|
||||
"""Installs thread-safe spies on NPU methods to track invocation counts."""
|
||||
if metrics is None:
|
||||
return contextlib.nullcontext()
|
||||
|
||||
from vllm_ascend.worker.model_runner_v1 import NPUModelRunner
|
||||
|
||||
def make_spy(cls, method_name, capture_mem):
|
||||
original = getattr(cls, method_name)
|
||||
|
||||
def spy(self, *args, **kwargs):
|
||||
mem_before = torch.npu.mem_get_info()[0] # free memory
|
||||
result = original(self, *args, **kwargs)
|
||||
mem_after = torch.npu.mem_get_info()[0]
|
||||
with capture_mem["call_counts"].get_lock():
|
||||
capture_mem["call_counts"].value += 1
|
||||
capture_mem["mem_before_capture"].value += mem_before
|
||||
capture_mem["mem_after_capture"].value += mem_after
|
||||
return result
|
||||
|
||||
return spy
|
||||
|
||||
stack = contextlib.ExitStack()
|
||||
hooks = [
|
||||
(NPUModelRunner, "capture_model", metrics["capture_mem"]),
|
||||
]
|
||||
|
||||
for cls, method, metric in hooks:
|
||||
stack.enter_context(patch.object(cls, method, make_spy(cls, method, metric)))
|
||||
|
||||
return stack
|
||||
|
||||
|
||||
def _check_prefill_token(baseline, comp_ids, comp_logprobs, prompt_idx: int, atol: float) -> None:
|
||||
"""Token 0 is produced by the prefill pass; both models see identical input,
|
||||
so the chosen token *must* be the same and its logprob must match within atol."""
|
||||
base_token_id = baseline["token_ids"][0]
|
||||
comp_token_id = comp_ids[0]
|
||||
assert base_token_id == comp_token_id, (
|
||||
f"Prefill token mismatch at prompt {prompt_idx}: baseline={base_token_id}, compiled={comp_token_id}"
|
||||
)
|
||||
base_logprob = baseline["logprobs"][0]
|
||||
comp_logprob = comp_logprobs[0][comp_token_id]
|
||||
assert abs(base_logprob - comp_logprob) <= atol, (
|
||||
f"Prefill logprob mismatch at prompt {prompt_idx}: "
|
||||
f"baseline={base_logprob:.4f}, compiled={comp_logprob:.4f}, "
|
||||
f"diff={abs(base_logprob - comp_logprob):.4f} > atol={atol}"
|
||||
)
|
||||
|
||||
|
||||
def _check_decode_token(baseline, comp_ids, comp_logprobs, token_idx: int, prompt_idx: int, decode_atol: float) -> None:
|
||||
"""Tokens 1-2 come from decode passes. When the two models pick different
|
||||
tokens the context has already diverged, so we cannot compare logprobs of
|
||||
the chosen tokens directly. Instead we do a cross-lookup: find the
|
||||
baseline's chosen token inside compiled's top-K distribution (and vice
|
||||
versa) and assert that the assigned log-probability is close. This
|
||||
confirms that the compiled model's distribution is numerically consistent
|
||||
with the baseline's even when the argmax differs by a tiny margin.
|
||||
"""
|
||||
base_token_id = baseline["token_ids"][token_idx]
|
||||
comp_token_id = comp_ids[token_idx]
|
||||
base_logprob = baseline["logprobs"][token_idx]
|
||||
comp_topk = comp_logprobs[token_idx]
|
||||
if base_token_id == comp_token_id:
|
||||
# Happy path: same token, direct logprob comparison.
|
||||
diff = abs(base_logprob - comp_topk[comp_token_id])
|
||||
assert diff <= decode_atol, (
|
||||
f"Decode logprob mismatch at prompt {prompt_idx}, token {token_idx}: "
|
||||
f"baseline={base_logprob:.4f}, "
|
||||
f"compiled={comp_topk[comp_token_id]:.4f}, "
|
||||
f"diff={diff:.4f} > decode_atol={decode_atol}"
|
||||
)
|
||||
return
|
||||
|
||||
# Tokens differ – cross-lookup in each model's top-K distribution.
|
||||
comp_logprob = comp_topk[comp_token_id]
|
||||
# Check: what log-probability did compiled assign to baseline's token?
|
||||
assert base_token_id in comp_topk, (
|
||||
f"Decode token mismatch at prompt {prompt_idx}, token {token_idx}: "
|
||||
f"baseline chose token {base_token_id} (logprob={base_logprob:.4f}) but "
|
||||
f"compiled chose token {comp_token_id} (logprob={comp_logprob:.4f}) and "
|
||||
f"baseline's token does not appear in compiled's top-{_SAMPLING_PARAMS.logprobs} distribution"
|
||||
)
|
||||
comp_logprob_of_base_token = comp_topk[base_token_id]
|
||||
diff = abs(base_logprob - comp_logprob_of_base_token)
|
||||
assert diff <= decode_atol, (
|
||||
f"Decode distribution mismatch at prompt {prompt_idx}, token {token_idx}: "
|
||||
f"baseline chose token {base_token_id} with logprob={base_logprob:.4f}; "
|
||||
f"compiled assigned logprob={comp_logprob_of_base_token:.4f} to that token, "
|
||||
f"diff={diff:.4f} > decode_atol={decode_atol} "
|
||||
f"(compiled chose token {comp_token_id} with logprob={comp_logprob:.4f})"
|
||||
)
|
||||
|
||||
|
||||
def _run_worker_process(
|
||||
rank: int,
|
||||
local_rank: int,
|
||||
world_size: int,
|
||||
cur_case: dict,
|
||||
master_ip: str,
|
||||
master_port: int,
|
||||
result_queue: multiprocessing.Queue,
|
||||
metrics: dict[str, Any] | None = None,
|
||||
):
|
||||
"""Main entry point for the worker process."""
|
||||
os.environ.update(
|
||||
{
|
||||
"VLLM_DP_RANK": str(rank),
|
||||
"VLLM_DP_RANK_LOCAL": str(local_rank),
|
||||
"VLLM_DP_SIZE": str(world_size),
|
||||
"VLLM_DP_MASTER_IP": master_ip,
|
||||
"VLLM_DP_MASTER_PORT": str(master_port),
|
||||
}
|
||||
)
|
||||
|
||||
for key, value in cur_case.get("env_vars", {}).items():
|
||||
os.environ[key] = str(value)
|
||||
|
||||
# Apply hooks and run inference
|
||||
with _install_spies(metrics):
|
||||
short_prompts = cur_case["prompts"]["short"]
|
||||
chunk_size = len(short_prompts) // world_size
|
||||
short_start_idx = rank * chunk_size
|
||||
short_end_idx = short_start_idx + chunk_size if rank < world_size - 1 else len(short_prompts)
|
||||
local_short_prompts = short_prompts[short_start_idx:short_end_idx]
|
||||
|
||||
long_prompts = cur_case["prompts"]["long"]
|
||||
chunk_size = len(long_prompts) // world_size
|
||||
long_start_idx = rank * chunk_size
|
||||
long_end_idx = long_start_idx + chunk_size if rank < world_size - 1 else len(long_prompts)
|
||||
local_long_prompts = long_prompts[long_start_idx:long_end_idx]
|
||||
|
||||
llm = LLM(
|
||||
model=cur_case["model"],
|
||||
max_model_len=1024,
|
||||
compilation_config=cur_case["compilation_config"],
|
||||
quantization=cur_case["quantization"],
|
||||
tensor_parallel_size=cur_case["tensor_parallel_size"],
|
||||
enable_expert_parallel=cur_case["enable_expert_parallel"],
|
||||
trust_remote_code=True,
|
||||
)
|
||||
|
||||
compiled_outputs_short = llm.generate(local_short_prompts, _SAMPLING_PARAMS)
|
||||
compiled_outputs_long = llm.generate(local_long_prompts, _SAMPLING_PARAMS)
|
||||
|
||||
def extract_outputs(outputs):
|
||||
extracted = []
|
||||
for out in outputs:
|
||||
gen = out.outputs[0]
|
||||
extracted.append(
|
||||
{
|
||||
"text": gen.text,
|
||||
"token_ids": list(gen.token_ids),
|
||||
"logprobs": [
|
||||
{token_id: lp.logprob for token_id, lp in step_logprobs.items()}
|
||||
for step_logprobs in gen.logprobs
|
||||
]
|
||||
if gen.logprobs
|
||||
else None,
|
||||
}
|
||||
)
|
||||
return extracted
|
||||
|
||||
result_data = {
|
||||
"rank": rank,
|
||||
"short": {"prompt_idx": short_start_idx, "outputs": extract_outputs(compiled_outputs_short)},
|
||||
"long": {"prompt_idx": long_start_idx, "outputs": extract_outputs(compiled_outputs_long)},
|
||||
}
|
||||
result_queue.put(result_data)
|
||||
|
||||
|
||||
def _exit():
|
||||
from vllm_ascend.ascend_config import clear_ascend_config
|
||||
|
||||
clear_ascend_config()
|
||||
cleanup_dist_env_and_memory()
|
||||
|
||||
|
||||
def check_accuracy(baselines, result, atol, decode_atol):
|
||||
for idx, comp_out in enumerate(result["outputs"]):
|
||||
prompt_idx = result["prompt_idx"] + idx
|
||||
baseline = baselines[prompt_idx]
|
||||
comp_ids = comp_out["token_ids"]
|
||||
comp_logprobs = comp_out["logprobs"]
|
||||
|
||||
assert comp_logprobs is not None, f"logprobs not returned for prompt {prompt_idx}"
|
||||
assert len(baseline["token_ids"]) == len(comp_ids) == _SAMPLING_PARAMS.max_tokens, (
|
||||
f"Expected {_SAMPLING_PARAMS.max_tokens} tokens for prompt {prompt_idx}, "
|
||||
f"got baseline={len(baseline['token_ids'])}, compiled={len(comp_ids)}"
|
||||
)
|
||||
_check_prefill_token(baseline, comp_ids, comp_logprobs, prompt_idx, atol)
|
||||
for token_idx in range(1, _SAMPLING_PARAMS.max_tokens):
|
||||
_check_decode_token(baseline, comp_ids, comp_logprobs, token_idx, prompt_idx, decode_atol)
|
||||
|
||||
|
||||
def check_capture_mem(capture_mem, baseline_capture_mem=0.2, capture_mem_tolerance=1.3):
|
||||
assert capture_mem["call_counts"].value != 0, (
|
||||
f"capture_model was not called during test. capture_called({capture_mem['call_counts'].value})"
|
||||
)
|
||||
|
||||
print("capture_called =", capture_mem["call_counts"].value)
|
||||
print("capture_mem_before =", capture_mem["mem_before_capture"].value)
|
||||
print("capture_mem_after =", capture_mem["mem_after_capture"].value)
|
||||
|
||||
mem_used_by_capture = (
|
||||
capture_mem["mem_before_capture"].value - capture_mem["mem_after_capture"].value
|
||||
) / capture_mem["call_counts"].value
|
||||
# Empirical observation: capturing ACL graphs for Qwen3-0.6B uses ~0.20 GiB of NPU memory.
|
||||
# DeepSeek-V2-Lite-W8A8 uses ~0.68 GiB of NPU memory
|
||||
# a 1.3x tolerance is applied to account for runtime variance.
|
||||
max_capture_mem_gib = baseline_capture_mem * capture_mem_tolerance
|
||||
max_mem_expected = max_capture_mem_gib * (1024**3)
|
||||
assert mem_used_by_capture < max_mem_expected, (
|
||||
f"capture_model used more memory than expected. "
|
||||
f"Used: {mem_used_by_capture / (1024**3):.2f} GiB, "
|
||||
f"Expected: < {max_capture_mem_gib:.2f} GiB"
|
||||
)
|
||||
|
||||
|
||||
@wait_until_npu_memory_free(0.7)
|
||||
@pytest.mark.parametrize("cur_case", [CASE_QWEN_ACLGRAPH, CASE_DS_ACLGRAPH, CASE_DS_ACLGRAPH_ENPU])
|
||||
def test_aclgraph(cur_case: dict, monkeypatch: pytest.MonkeyPatch):
|
||||
# Counter doesn't work in default "spawn" mode
|
||||
metrics = None
|
||||
if "DeepSeek-V2-Lite-W8A8" in cur_case["model"]:
|
||||
# TODO(shihan-lin168): remove this env after set_device issue is resolved
|
||||
monkeypatch.setenv("VLLM_WORKER_MULTIPROC_METHOD", "spawn")
|
||||
|
||||
else:
|
||||
monkeypatch.setenv("OMP_NUM_THREADS", "1")
|
||||
monkeypatch.delenv("VLLM_WORKER_MULTIPROC_METHOD", raising=False)
|
||||
metrics = {
|
||||
"capture_mem": {
|
||||
# Monitor key metrics of mem
|
||||
"call_counts": multiprocessing.Value("i", 0),
|
||||
"mem_before_capture": multiprocessing.Value("q", 0),
|
||||
"mem_after_capture": multiprocessing.Value("q", 0),
|
||||
},
|
||||
}
|
||||
|
||||
port = get_open_port()
|
||||
|
||||
# Create a queue to collect results from both processes
|
||||
result_queue: multiprocessing.Queue[dict] = multiprocessing.Queue()
|
||||
|
||||
# Launch workers
|
||||
workers = []
|
||||
for rank in range(cur_case["data_parallel_size"]):
|
||||
p = multiprocessing.Process(
|
||||
target=_run_worker_process,
|
||||
args=(rank, rank, cur_case["data_parallel_size"], cur_case, "127.0.0.1", port, result_queue, metrics),
|
||||
)
|
||||
p.start()
|
||||
workers.append(p)
|
||||
|
||||
all_dp_results = []
|
||||
|
||||
# get results
|
||||
for _ in range(cur_case["data_parallel_size"]):
|
||||
try:
|
||||
result = result_queue.get(timeout=180)
|
||||
all_dp_results.append(result)
|
||||
except queue.Empty:
|
||||
print("Error: Timeout waiting for worker results. A worker might have crashed.")
|
||||
break
|
||||
|
||||
# Supervision loop
|
||||
for p in workers:
|
||||
p.join(timeout=30)
|
||||
if p.exitcode != 0:
|
||||
for k in workers:
|
||||
if k.is_alive():
|
||||
k.kill()
|
||||
p.join(timeout=5)
|
||||
raise RuntimeError(f"Worker {p.pid} failed with exit code {p.exitcode}")
|
||||
_exit()
|
||||
assert len(all_dp_results) == cur_case["data_parallel_size"], f"Expected 2 results, got {len(all_dp_results)}"
|
||||
|
||||
# check graph memory
|
||||
if metrics is not None:
|
||||
check_capture_mem(metrics["capture_mem"], cur_case["baseline_capture_mem"], cur_case["capture_mem_tolerance"])
|
||||
|
||||
# check accuracy
|
||||
decode_atol = 2 * ATOL
|
||||
for result in all_dp_results:
|
||||
check_accuracy(cur_case["golden_answers"]["short"], result["short"], ATOL, decode_atol)
|
||||
check_accuracy(cur_case["golden_answers"]["long"], result["long"], ATOL, decode_atol)
|
||||
146
tests/e2e/pull_request/four_card/test_pipeline_parallel.py
Normal file
146
tests/e2e/pull_request/four_card/test_pipeline_parallel.py
Normal file
@@ -0,0 +1,146 @@
|
||||
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
# This file is a part of the vllm-ascend project.
|
||||
#
|
||||
import pytest
|
||||
|
||||
from tests.e2e.conftest import DPVllmRunner, VllmRunner, wait_until_npu_memory_free
|
||||
from tests.e2e.model_utils import check_outputs_equal
|
||||
|
||||
DS3 = "deepseek-ai/DeepSeek-V2-Lite-Chat"
|
||||
MODELS = [
|
||||
DS3,
|
||||
]
|
||||
MOE_MODELS = [
|
||||
DS3,
|
||||
]
|
||||
|
||||
DATA_PARALLELS = [2]
|
||||
TENSOR_PARALLELS = [1]
|
||||
PIPELINE_PARALLELS = [2]
|
||||
DIST_EXECUTOR_BACKEND = ["mp", "ray"]
|
||||
|
||||
prompts = [
|
||||
"Hello, my name is",
|
||||
"The future of AI is",
|
||||
]
|
||||
GOLDEN = [
|
||||
(
|
||||
[
|
||||
17464,
|
||||
11,
|
||||
601,
|
||||
1210,
|
||||
317,
|
||||
459,
|
||||
6946,
|
||||
29,
|
||||
32,
|
||||
1568,
|
||||
32092,
|
||||
535,
|
||||
6946,
|
||||
29,
|
||||
285,
|
||||
304,
|
||||
6,
|
||||
76,
|
||||
245,
|
||||
459,
|
||||
6946,
|
||||
],
|
||||
"Hello, my name is <strong>Alessandro</strong> and I'm a <strong",
|
||||
),
|
||||
(
|
||||
[
|
||||
549,
|
||||
3680,
|
||||
280,
|
||||
20838,
|
||||
317,
|
||||
6464,
|
||||
11,
|
||||
285,
|
||||
359,
|
||||
487,
|
||||
82,
|
||||
1872,
|
||||
276,
|
||||
330,
|
||||
245,
|
||||
2624,
|
||||
12,
|
||||
73309,
|
||||
279,
|
||||
254,
|
||||
1843,
|
||||
],
|
||||
"The future of AI is bright, and it’s going to be a game-changer in the world",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", MODELS)
|
||||
@pytest.mark.parametrize("tp_size", TENSOR_PARALLELS)
|
||||
@pytest.mark.parametrize("pp_size", PIPELINE_PARALLELS)
|
||||
@pytest.mark.parametrize("distributed_executor_backend", DIST_EXECUTOR_BACKEND)
|
||||
@wait_until_npu_memory_free(target_free_percentage=0.6)
|
||||
def test_models_pp2_tp2(model: str, tp_size: int, pp_size: int, distributed_executor_backend: str) -> None:
|
||||
with VllmRunner(
|
||||
model,
|
||||
tensor_parallel_size=tp_size,
|
||||
pipeline_parallel_size=pp_size,
|
||||
compilation_config={
|
||||
"cudagraph_mode": "PIECEWISE",
|
||||
"cudagraph_capture_sizes": [1, 2, 4],
|
||||
},
|
||||
distributed_executor_backend=distributed_executor_backend,
|
||||
gpu_memory_utilization=0.7,
|
||||
enable_expert_parallel=model in MOE_MODELS,
|
||||
) as vllm_model:
|
||||
outputs = vllm_model.generate_greedy(prompts, 16)
|
||||
check_outputs_equal(
|
||||
outputs_0_lst=outputs,
|
||||
outputs_1_lst=GOLDEN,
|
||||
name_0=f"{model}-tp{tp_size}pp{pp_size}",
|
||||
name_1="GOLDEN",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", MODELS)
|
||||
@pytest.mark.parametrize("dp_size", DATA_PARALLELS)
|
||||
@pytest.mark.parametrize("pp_size", PIPELINE_PARALLELS)
|
||||
@pytest.mark.parametrize("distributed_executor_backend", DIST_EXECUTOR_BACKEND)
|
||||
@wait_until_npu_memory_free(target_free_percentage=0.6)
|
||||
def test_models_pp2_dp2(model: str, dp_size: int, pp_size: int, distributed_executor_backend: str) -> None:
|
||||
with DPVllmRunner(
|
||||
model,
|
||||
data_parallel_size=dp_size,
|
||||
pipeline_parallel_size=pp_size,
|
||||
compilation_config={
|
||||
"cudagraph_mode": "PIECEWISE",
|
||||
"cudagraph_capture_sizes": [1, 2, 4],
|
||||
},
|
||||
distributed_executor_backend=distributed_executor_backend,
|
||||
gpu_memory_utilization=0.7,
|
||||
enable_expert_parallel=model in MOE_MODELS,
|
||||
) as vllm_model:
|
||||
outputs = vllm_model.generate_greedy(prompts, 16)
|
||||
check_outputs_equal(
|
||||
outputs_0_lst=outputs,
|
||||
outputs_1_lst=GOLDEN,
|
||||
name_0=f"{model}-dp{dp_size}pp{pp_size}",
|
||||
name_1="GOLDEN",
|
||||
)
|
||||
@@ -0,0 +1,97 @@
|
||||
"""Performance guard for profiling-based dynamic chunk sizing (PP scenario).
|
||||
|
||||
Measures Time-To-First-Token (TTFT) on 64k-token prefill requests with
|
||||
profiling_chunk_config enabled. The test runs against
|
||||
DeepSeek-V2-Lite-Chat served with PP=2, TP=2 (4 NPU cards total).
|
||||
|
||||
Test flow:
|
||||
1. Create an LLM engine with profiling_chunk_config enabled.
|
||||
2. Run NUM_WARMUP sequential requests (64k tokens, max_tokens=1) to warm
|
||||
up both the NPU and the profiling predictor.
|
||||
3. Run NUM_TEST sequential requests, recording TTFT for each.
|
||||
4. Assert that the median TTFT does not exceed BASELINE_TTFT_S seconds.
|
||||
"""
|
||||
|
||||
import os
|
||||
import statistics
|
||||
import time
|
||||
from unittest.mock import patch
|
||||
|
||||
from tests.e2e.conftest import VllmRunner
|
||||
|
||||
MODEL = "Qwen/Qwen3-30B-A3B"
|
||||
|
||||
# ~64k tokens
|
||||
_WORD = "hello "
|
||||
INPUT_64K_TOKENS = _WORD * (384_000 // len(_WORD))
|
||||
|
||||
NUM_WARMUP = 5
|
||||
NUM_TEST = 5
|
||||
|
||||
# NOTE: Any changes to this baseline must be approved by team members.
|
||||
# Measured on Qwen3-30B-A3B, PP=2, TP=2, 64k prefill, profiling_chunk enabled.
|
||||
BASELINE_TTFT_S = 5.2
|
||||
|
||||
|
||||
@patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"VLLM_WORKER_MULTIPROC_METHOD": "spawn",
|
||||
"VLLM_ALLOW_LONG_MAX_MODEL_LEN": "1",
|
||||
},
|
||||
)
|
||||
def test_profiling_chunk_ttft_performance() -> None:
|
||||
with VllmRunner(
|
||||
MODEL,
|
||||
max_model_len=70000,
|
||||
tensor_parallel_size=2,
|
||||
pipeline_parallel_size=2,
|
||||
block_size=128,
|
||||
enable_expert_parallel=True,
|
||||
enable_prefix_caching=False,
|
||||
gpu_memory_utilization=0.9,
|
||||
max_num_batched_tokens=12288,
|
||||
distributed_executor_backend="mp",
|
||||
enforce_eager=True,
|
||||
async_scheduling=False,
|
||||
additional_config={
|
||||
"profiling_chunk_config": {"enabled": True, "smooth_factor": 0.9},
|
||||
"enable_cpu_binding": False,
|
||||
"enable_flashcomm1": True,
|
||||
},
|
||||
hf_overrides={
|
||||
"rope_parameters": {
|
||||
"rope_type": "yarn",
|
||||
"rope_theta": 1000,
|
||||
"factor": 5,
|
||||
"original_max_position_embeddings": 262144,
|
||||
}
|
||||
},
|
||||
) as vllm_model:
|
||||
# With max_tokens=1, total latency ≈ prefill time ≈ TTFT
|
||||
prompts = [INPUT_64K_TOKENS]
|
||||
|
||||
# ── Warmup ──────────────────────────────────────────────────────────
|
||||
for _ in range(NUM_WARMUP):
|
||||
vllm_model.generate_greedy(prompts, max_tokens=1)
|
||||
|
||||
# ── Measurement ─────────────────────────────────────────────────────
|
||||
ttfts: list[float] = []
|
||||
for _ in range(NUM_TEST):
|
||||
start = time.perf_counter()
|
||||
vllm_model.generate_greedy(prompts, max_tokens=1)
|
||||
ttfts.append(time.perf_counter() - start)
|
||||
|
||||
median_ttft = statistics.median(ttfts)
|
||||
ttft_str = ", ".join(f"{t:.2f}s" for t in ttfts)
|
||||
print(
|
||||
f"\n[profiling_chunk perf] TTFT per request: [{ttft_str}]"
|
||||
f"\n[profiling_chunk perf] Median TTFT: {median_ttft:.2f}s "
|
||||
f"(baseline: {BASELINE_TTFT_S}s)"
|
||||
)
|
||||
|
||||
assert median_ttft <= BASELINE_TTFT_S, (
|
||||
f"TTFT performance regression: median TTFT {median_ttft:.2f}s "
|
||||
f"exceeds baseline {BASELINE_TTFT_S}s. "
|
||||
f"Individual TTFTs: [{ttft_str}]"
|
||||
)
|
||||
70
tests/e2e/pull_request/four_card/test_qwen3_5.py
Normal file
70
tests/e2e/pull_request/four_card/test_qwen3_5.py
Normal file
@@ -0,0 +1,70 @@
|
||||
#
|
||||
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
# This file is a part of the vllm-ascend project.
|
||||
# Adapted from vllm/tests/basic_correctness/test_basic_correctness.py
|
||||
#
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
from tests.e2e.conftest import DPVllmRunner, VllmRunner
|
||||
|
||||
|
||||
def test_qwen3_5_27b_distributed_mp_tp4():
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
] * 4
|
||||
max_tokens = 5
|
||||
with VllmRunner(
|
||||
"Qwen/Qwen3.5-27B",
|
||||
tensor_parallel_size=4,
|
||||
cudagraph_capture_sizes=[1, 2, 4, 8],
|
||||
max_model_len=4096,
|
||||
gpu_memory_utilization=0.90,
|
||||
distributed_executor_backend="mp",
|
||||
) as vllm_model:
|
||||
vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
del vllm_model
|
||||
|
||||
|
||||
@patch.dict(os.environ, {"VLLM_ASCEND_ENABLE_FLASHCOMM1": "1"})
|
||||
def test_qwen3_5_35b_distributed_mp_tp4_full_decode_only_mtp3_flashcomm():
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
"The president of the United States is",
|
||||
"The capital of France is",
|
||||
"The future of AI is",
|
||||
]
|
||||
|
||||
max_tokens = 20
|
||||
with DPVllmRunner(
|
||||
"Qwen/Qwen3.5-35B-A3B",
|
||||
data_parallel_size=2,
|
||||
tensor_parallel_size=2,
|
||||
enable_expert_parallel=True,
|
||||
max_model_len=4096,
|
||||
gpu_memory_utilization=0.90,
|
||||
distributed_executor_backend="mp",
|
||||
compilation_config={
|
||||
"cudagraph_mode": "FULL_DECODE_ONLY",
|
||||
"cudagraph_capture_sizes": [4, 8, 12, 16],
|
||||
},
|
||||
speculative_config={
|
||||
"method": "qwen3_5_mtp",
|
||||
"num_speculative_tokens": 3,
|
||||
},
|
||||
) as vllm_model:
|
||||
vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
del vllm_model
|
||||
80
tests/e2e/pull_request/four_card/test_qwen3_next.py
Normal file
80
tests/e2e/pull_request/four_card/test_qwen3_next.py
Normal file
@@ -0,0 +1,80 @@
|
||||
#
|
||||
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||
# Copyright 2023 The vLLM team.
|
||||
#
|
||||
# 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.
|
||||
# This file is a part of the vllm-ascend project.
|
||||
# Adapted from vllm/tests/basic_correctness/test_basic_correctness.py
|
||||
#
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
from tests.e2e.conftest import VllmRunner
|
||||
|
||||
|
||||
def test_qwen3_next_distributed_mp_full_decode_only_tp4():
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
] * 4
|
||||
max_tokens = 5
|
||||
with VllmRunner(
|
||||
"Qwen/Qwen3-Next-80B-A3B-Instruct",
|
||||
tensor_parallel_size=4,
|
||||
max_model_len=4096,
|
||||
gpu_memory_utilization=0.8,
|
||||
distributed_executor_backend="mp",
|
||||
compilation_config={"cudagraph_mode": "FULL_DECODE_ONLY", "cudagraph_capture_sizes": [1, 2, 4]},
|
||||
) as vllm_model:
|
||||
vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
del vllm_model
|
||||
|
||||
|
||||
@patch.dict(os.environ, {"VLLM_ASCEND_ENABLE_FLASHCOMM1": "1"})
|
||||
@patch.dict(os.environ, {"HCCL_BUFFSIZE": "1024"})
|
||||
def test_qwen3_next_w8a8dynamic_distributed_mp_flash_comm_tp4():
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
] * 4
|
||||
max_tokens = 5
|
||||
with VllmRunner(
|
||||
"vllm-ascend/Qwen3-Next-80B-A3B-Instruct-W8A8",
|
||||
tensor_parallel_size=4,
|
||||
max_model_len=4096,
|
||||
gpu_memory_utilization=0.7,
|
||||
distributed_executor_backend="mp",
|
||||
enable_expert_parallel=True,
|
||||
enforce_eager=True,
|
||||
quantization="ascend",
|
||||
) as vllm_model:
|
||||
vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
del vllm_model
|
||||
|
||||
|
||||
@patch.dict(os.environ, {"HCCL_BUFFSIZE": "1024"})
|
||||
def test_qwen3_next_distributed_mp_graph_mode_tp4():
|
||||
example_prompts = [
|
||||
"Hello, my name is",
|
||||
] * 4
|
||||
max_tokens = 5
|
||||
with VllmRunner(
|
||||
"Qwen/Qwen3-Next-80B-A3B-Instruct",
|
||||
tensor_parallel_size=4,
|
||||
max_model_len=4096,
|
||||
gpu_memory_utilization=0.8,
|
||||
distributed_executor_backend="mp",
|
||||
enable_expert_parallel=True,
|
||||
cudagraph_capture_sizes=[1, 2, 4],
|
||||
enforce_eager=False,
|
||||
) as vllm_model:
|
||||
vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||
del vllm_model
|
||||
Reference in New Issue
Block a user