@@ -297,7 +297,7 @@ jobs:
|
|||||||
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }}
|
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }}
|
||||||
uses: ./.github/workflows/_npu-single-node-test-stage.yml
|
uses: ./.github/workflows/_npu-single-node-test-stage.yml
|
||||||
with:
|
with:
|
||||||
runner: linux-aarch64-a3-2-
|
runner: linux-aarch64-a3-800t-2
|
||||||
test_type: 'accuracy'
|
test_type: 'accuracy'
|
||||||
test_suite: base-c-test-acc-2-npu-a3
|
test_suite: base-c-test-acc-2-npu-a3
|
||||||
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
|
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
|
||||||
@@ -310,7 +310,7 @@ jobs:
|
|||||||
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }}
|
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }}
|
||||||
uses: ./.github/workflows/_npu-single-node-test-stage.yml
|
uses: ./.github/workflows/_npu-single-node-test-stage.yml
|
||||||
with:
|
with:
|
||||||
runner: linux-aarch64-a3-4-
|
runner: linux-aarch64-a3-800t-4
|
||||||
test_type: 'accuracy'
|
test_type: 'accuracy'
|
||||||
test_suite: base-c-test-acc-4-npu-a3
|
test_suite: base-c-test-acc-4-npu-a3
|
||||||
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
|
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
|
||||||
@@ -323,7 +323,7 @@ jobs:
|
|||||||
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }}
|
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }}
|
||||||
uses: ./.github/workflows/_npu-single-node-test-stage.yml
|
uses: ./.github/workflows/_npu-single-node-test-stage.yml
|
||||||
with:
|
with:
|
||||||
runner: linux-aarch64-a3-8-
|
runner: linux-aarch64-a3-800t-8
|
||||||
test_type: 'accuracy'
|
test_type: 'accuracy'
|
||||||
test_suite: base-c-test-acc-8-npu-a3
|
test_suite: base-c-test-acc-8-npu-a3
|
||||||
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
|
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
|
||||||
@@ -336,7 +336,7 @@ jobs:
|
|||||||
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }}
|
if: ${{ !failure() && !cancelled() && needs.check-changes.outputs.main_package == 'true' }}
|
||||||
uses: ./.github/workflows/_npu-single-node-test-stage.yml
|
uses: ./.github/workflows/_npu-single-node-test-stage.yml
|
||||||
with:
|
with:
|
||||||
runner: linux-aarch64-a3-16-
|
runner: linux-aarch64-a3-800t-16
|
||||||
test_type: 'accuracy'
|
test_type: 'accuracy'
|
||||||
test_suite: base-c-test-acc-16-npu-a3
|
test_suite: base-c-test-acc-16-npu-a3
|
||||||
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
|
image: ${{ needs.set-image-config.outputs.CANN_image_a3 }}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import signal
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
@@ -17,6 +16,7 @@ from sglang.srt.utils import kill_process_tree
|
|||||||
from sglang.test.ascend.e2e.test_npu_multi_node_utils import (
|
from sglang.test.ascend.e2e.test_npu_multi_node_utils import (
|
||||||
SERVICE_PORT,
|
SERVICE_PORT,
|
||||||
check_role,
|
check_role,
|
||||||
|
kill_process_group,
|
||||||
launch_pd_mix_node,
|
launch_pd_mix_node,
|
||||||
launch_pd_separation_node,
|
launch_pd_separation_node,
|
||||||
launch_router,
|
launch_router,
|
||||||
@@ -105,21 +105,6 @@ def get_max_retries(datasets):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
def _kill_evalscope_session(process):
|
|
||||||
"""Kill the whole evalscope session (process.pid is the leader == pgid)."""
|
|
||||||
if process is None:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
os.killpg(process.pid, signal.SIGKILL)
|
|
||||||
logger.info(f"run_evalscope killed session pgid={process.pid}")
|
|
||||||
except ProcessLookupError:
|
|
||||||
logger.info(f"run_evalscope session pgid={process.pid} already gone")
|
|
||||||
except PermissionError:
|
|
||||||
logger.warning(
|
|
||||||
f"run_evalscope no permission to kill session pgid={process.pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def run_evalscope(
|
def run_evalscope(
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
@@ -215,7 +200,7 @@ def run_evalscope(
|
|||||||
f"returncode={process.returncode}"
|
f"returncode={process.returncode}"
|
||||||
)
|
)
|
||||||
|
|
||||||
_kill_evalscope_session(process)
|
kill_process_group(process)
|
||||||
|
|
||||||
if process.returncode != 0:
|
if process.returncode != 0:
|
||||||
logger.error(f"Command failed with return code: {process.returncode}")
|
logger.error(f"Command failed with return code: {process.returncode}")
|
||||||
@@ -272,14 +257,14 @@ def run_evalscope(
|
|||||||
logger.warning("Process did not terminate gracefully, killing it...")
|
logger.warning("Process did not terminate gracefully, killing it...")
|
||||||
process.kill()
|
process.kill()
|
||||||
logger.info("Process killed")
|
logger.info("Process killed")
|
||||||
_kill_evalscope_session(process)
|
kill_process_group(process)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error executing command: {e}")
|
logger.error(f"Error executing command: {e}")
|
||||||
process.terminate()
|
process.terminate()
|
||||||
process.wait(timeout=5)
|
process.wait(timeout=5)
|
||||||
_kill_evalscope_session(process)
|
kill_process_group(process)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import signal
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
@@ -1079,3 +1080,22 @@ class TestNpuMultiNodePdSepTestCaseBase(CustomTestCase):
|
|||||||
expect_accuracy,
|
expect_accuracy,
|
||||||
f'Accuracy is {str(metrics["accuracy"])}, is lower than {expect_accuracy}',
|
f'Accuracy is {str(metrics["accuracy"])}, is lower than {expect_accuracy}',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def kill_process_group(process):
|
||||||
|
"""SIGKILL the whole process group led by ``process.pid`` (== pgid).
|
||||||
|
|
||||||
|
``process`` is a ``subprocess.Popen`` launched with ``start_new_session=True``,
|
||||||
|
so its pid equals the process-group id. A ``None`` process is ignored.
|
||||||
|
"""
|
||||||
|
if process is None:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
os.killpg(process.pid, signal.SIGKILL)
|
||||||
|
logger.info(f"killed process group pgid={process.pid}")
|
||||||
|
except ProcessLookupError:
|
||||||
|
logger.info(f"process group pgid={process.pid} already gone")
|
||||||
|
except PermissionError:
|
||||||
|
logger.warning(f"no permission to kill process group pgid={process.pid}")
|
||||||
|
except OSError as e:
|
||||||
|
logger.warning(f"failed to kill process group pgid={process.pid}: {e}")
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from sglang.test.ascend.e2e.test_npu_multi_node_utils import (
|
|||||||
NAMESPACE,
|
NAMESPACE,
|
||||||
SERVICE_PORT,
|
SERVICE_PORT,
|
||||||
check_role,
|
check_role,
|
||||||
|
kill_process_group,
|
||||||
launch_pd_mix_node,
|
launch_pd_mix_node,
|
||||||
launch_pd_separation_node,
|
launch_pd_separation_node,
|
||||||
launch_router,
|
launch_router,
|
||||||
@@ -205,6 +206,9 @@ MAX_SERVER_KEEP_ALIVE_TIME = 3600
|
|||||||
|
|
||||||
# Timeouts and delays
|
# Timeouts and delays
|
||||||
SERVER_INITIALIZATION_DELAY = 120
|
SERVER_INITIALIZATION_DELAY = 120
|
||||||
|
BENCHMARK_STDOUT_DRAIN_GRACE = 10 # Grace seconds to wait for the direct child to exit after it drains stdout / exits
|
||||||
|
BENCHMARK_STDOUT_IDLE_TIMEOUT = 300 # Idle timeout: no output for this long means the benchmark is stuck, then force-kill
|
||||||
|
BENCHMARK_WATCHDOG_POLL_INTERVAL = 30 # Watchdog polling interval (seconds)
|
||||||
|
|
||||||
# Test parameters
|
# Test parameters
|
||||||
PROMPTS_MULTIPLIER = 4
|
PROMPTS_MULTIPLIER = 4
|
||||||
@@ -474,6 +478,9 @@ def run_bench_serving(
|
|||||||
# Run benchmark command and capture output
|
# Run benchmark command and capture output
|
||||||
metrics = {"mean_ttft": None, "mean_tpot": None, "total_tps": None}
|
metrics = {"mean_ttft": None, "mean_tpot": None, "total_tps": None}
|
||||||
|
|
||||||
|
# Launch the benchmark in its own session/process group so a dead server
|
||||||
|
# cannot wedge the run: on timeout we nuke the whole group (including any
|
||||||
|
# re-parented descendants) instead of waiting forever on its stdout.
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
cmd_args,
|
cmd_args,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
@@ -481,11 +488,59 @@ def run_bench_serving(
|
|||||||
text=True,
|
text=True,
|
||||||
bufsize=1,
|
bufsize=1,
|
||||||
env=env,
|
env=env,
|
||||||
|
start_new_session=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
reader_done = threading.Event()
|
||||||
|
# Timestamp of the last output line; the watchdog reads it to detect a
|
||||||
|
# silent/stuck benchmark.
|
||||||
|
last_activity = time.time()
|
||||||
|
|
||||||
|
def _kill_on_timeout():
|
||||||
|
while True:
|
||||||
|
# Direct child has exited, but stdout may still be held open by a
|
||||||
|
# grandchild; wait for the reader to drain within the grace period,
|
||||||
|
# otherwise force-kill the whole process group.
|
||||||
|
if process.poll() is not None:
|
||||||
|
if not reader_done.wait(timeout=BENCHMARK_STDOUT_DRAIN_GRACE):
|
||||||
|
logger.error(
|
||||||
|
f"Benchmark stdout still open after process exit, "
|
||||||
|
f"killing process group {process.pid}"
|
||||||
|
)
|
||||||
|
kill_process_group(process)
|
||||||
|
return
|
||||||
|
# stdout has drained, but the direct child may still be alive:
|
||||||
|
# give it a bounded time to exit, otherwise nuke the group.
|
||||||
|
if reader_done.is_set():
|
||||||
|
try:
|
||||||
|
process.wait(timeout=BENCHMARK_STDOUT_DRAIN_GRACE)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
logger.error(
|
||||||
|
f"Benchmark {process.pid} still alive after stdout EOF, "
|
||||||
|
f"killing process group {process.pid}"
|
||||||
|
)
|
||||||
|
kill_process_group(process)
|
||||||
|
return
|
||||||
|
# No output for too long while still alive -> likely hung.
|
||||||
|
idle = time.time() - last_activity
|
||||||
|
if idle > BENCHMARK_STDOUT_IDLE_TIMEOUT:
|
||||||
|
logger.error(
|
||||||
|
f"Benchmark produced no output for {idle:.0f}s "
|
||||||
|
f"(> {BENCHMARK_STDOUT_IDLE_TIMEOUT}s), "
|
||||||
|
f"killing process group {process.pid}"
|
||||||
|
)
|
||||||
|
kill_process_group(process)
|
||||||
|
return
|
||||||
|
time.sleep(BENCHMARK_WATCHDOG_POLL_INTERVAL)
|
||||||
|
|
||||||
|
watchdog = threading.Thread(target=_kill_on_timeout, daemon=True)
|
||||||
|
watchdog.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Read output line by line
|
# Read output line by line
|
||||||
with open(result_file, "a", encoding="utf-8") as f:
|
with open(result_file, "a", encoding="utf-8") as f:
|
||||||
for line in process.stdout:
|
for line in process.stdout:
|
||||||
|
last_activity = time.time()
|
||||||
if line.strip():
|
if line.strip():
|
||||||
print(line, end="")
|
print(line, end="")
|
||||||
f.write(line)
|
f.write(line)
|
||||||
@@ -508,6 +563,7 @@ def run_bench_serving(
|
|||||||
parts = stripped_line.split()
|
parts = stripped_line.split()
|
||||||
if len(parts) >= 5:
|
if len(parts) >= 5:
|
||||||
metrics["mean_e2e_latency"] = parts[4]
|
metrics["mean_e2e_latency"] = parts[4]
|
||||||
|
reader_done.set()
|
||||||
process.wait()
|
process.wait()
|
||||||
if process.returncode != 0:
|
if process.returncode != 0:
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -516,6 +572,7 @@ def run_bench_serving(
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error running benchmark: {e}")
|
logger.error(f"Error running benchmark: {e}")
|
||||||
finally:
|
finally:
|
||||||
|
reader_done.set()
|
||||||
if process.stdout is not None and not process.stdout.closed:
|
if process.stdout is not None and not process.stdout.closed:
|
||||||
process.stdout.close()
|
process.stdout.close()
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -20,7 +20,7 @@ ENVS = {
|
|||||||
"HCCL_SOCKET_IFNAME": "lo",
|
"HCCL_SOCKET_IFNAME": "lo",
|
||||||
"GLOO_SOCKET_IFNAME": "lo",
|
"GLOO_SOCKET_IFNAME": "lo",
|
||||||
"HCCL_OP_EXPANSION_MODE": "AIV",
|
"HCCL_OP_EXPANSION_MODE": "AIV",
|
||||||
"HCCL_BUFFSIZE": "2000",
|
"DEEPEP_HCCL_BUFFSIZE": "2000",
|
||||||
}
|
}
|
||||||
|
|
||||||
OTHER_ARGS = [
|
OTHER_ARGS = [
|
||||||
@@ -54,7 +54,7 @@ OTHER_ARGS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class TestQwen3(TestNpuAccuracyTestCaseBase):
|
class TestQwen3_VL_30B_A3B_Thinking_MMMU(TestNpuAccuracyTestCaseBase):
|
||||||
model = QWEN3_VL_30B_A3B_THINKING_MODEL_PATH
|
model = QWEN3_VL_30B_A3B_THINKING_MODEL_PATH
|
||||||
envs = ENVS
|
envs = ENVS
|
||||||
other_args = OTHER_ARGS
|
other_args = OTHER_ARGS
|
||||||
|
|||||||
+3
-3
@@ -10,7 +10,7 @@ from sglang.test.ascend.e2e.test_npu_performance_utils import (
|
|||||||
from sglang.test.ci.ci_register import register_npu_ci
|
from sglang.test.ci.ci_register import register_npu_ci
|
||||||
|
|
||||||
register_npu_ci(est_time=3600, suite="base-c-test-perf-2-npu-a3")
|
register_npu_ci(est_time=3600, suite="base-c-test-perf-2-npu-a3")
|
||||||
register_npu_ci(est_time=6500, suite="nightly-acc-2-npu-a3", nightly=True)
|
register_npu_ci(est_time=12000, suite="nightly-acc-2-npu-a3", nightly=True)
|
||||||
|
|
||||||
_is_pr_pipeline = os.environ.get("GITHUB_EVENT_NAME") == "pull_request"
|
_is_pr_pipeline = os.environ.get("GITHUB_EVENT_NAME") == "pull_request"
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ ENVS = {
|
|||||||
"HCCL_SOCKET_IFNAME": "lo",
|
"HCCL_SOCKET_IFNAME": "lo",
|
||||||
"GLOO_SOCKET_IFNAME": "lo",
|
"GLOO_SOCKET_IFNAME": "lo",
|
||||||
"HCCL_OP_EXPANSION_MODE": "AIV",
|
"HCCL_OP_EXPANSION_MODE": "AIV",
|
||||||
"HCCL_BUFFSIZE": "2000",
|
"DEEPEP_HCCL_BUFFSIZE": "2000",
|
||||||
}
|
}
|
||||||
|
|
||||||
OTHER_ARGS = [
|
OTHER_ARGS = [
|
||||||
@@ -52,7 +52,7 @@ OTHER_ARGS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class TestQwen3(TestNpuAccuracyTestCaseBase):
|
class TestQwen3_VL_8B_Thinking_MMMU(TestNpuAccuracyTestCaseBase):
|
||||||
model = QWEN3_VL_8B_THINKING_MODEL_PATH
|
model = QWEN3_VL_8B_THINKING_MODEL_PATH
|
||||||
envs = ENVS
|
envs = ENVS
|
||||||
other_args = OTHER_ARGS
|
other_args = OTHER_ARGS
|
||||||
|
|||||||
+2
@@ -53,6 +53,8 @@ KIMI_K2_6_OTHER_ARGS = [
|
|||||||
6144,
|
6144,
|
||||||
"--max-prefill-tokens",
|
"--max-prefill-tokens",
|
||||||
65536,
|
65536,
|
||||||
|
"--max-total-tokens",
|
||||||
|
32256,
|
||||||
"--enable-multimodal",
|
"--enable-multimodal",
|
||||||
"--mm-attention-backend",
|
"--mm-attention-backend",
|
||||||
"ascend_attn",
|
"ascend_attn",
|
||||||
|
|||||||
Reference in New Issue
Block a user