Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

333 lines
13 KiB
Python

from __future__ import annotations
import hashlib
import logging
import time
import traceback
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import Any, Callable, Dict, Iterator, Optional, Tuple
import torch
from sglang.srt.constants import (
GPU_MEMORY_ALL_TYPES,
GPU_MEMORY_TYPE_CUDA_GRAPH,
GPU_MEMORY_TYPE_KV_CACHE,
GPU_MEMORY_TYPE_WEIGHTS,
)
from sglang.srt.disaggregation.utils import DisaggregationMode
from sglang.srt.managers.io_struct import (
CheckWeightsReqInput,
CheckWeightsReqOutput,
DestroyWeightsUpdateGroupReqInput,
DestroyWeightsUpdateGroupReqOutput,
GetWeightsByNameReqInput,
GetWeightsByNameReqOutput,
InitWeightsUpdateGroupReqInput,
InitWeightsUpdateGroupReqOutput,
ReleaseMemoryOccupationReqInput,
ReleaseMemoryOccupationReqOutput,
ResumeMemoryOccupationReqInput,
ResumeMemoryOccupationReqOutput,
UpdateWeightFromDiskReqInput,
UpdateWeightFromDiskReqOutput,
UpdateWeightsFromDistributedReqInput,
UpdateWeightsFromDistributedReqOutput,
UpdateWeightsFromIPCReqInput,
UpdateWeightsFromIPCReqOutput,
UpdateWeightsFromTensorReqInput,
UpdateWeightsFromTensorReqOutput,
)
logger = logging.getLogger(__name__)
def _get_draft_model_runner(draft_worker):
# DFlash / FrozenKVMTP workers expose draft_model_runner directly
runner = getattr(draft_worker, "draft_model_runner", None)
if runner is not None:
return runner
# EAGLEWorkerV2: _draft_worker.draft_runner
inner = getattr(draft_worker, "_draft_worker", None)
if inner is not None:
runner = getattr(inner, "draft_runner", None)
if runner is not None:
return runner
return None
def _merge_checksum_payloads(target: Dict, draft: Dict) -> Dict:
merged_checksums = dict(target["checksums"])
for name, chk in draft["checksums"].items():
merged_checksums[f"draft.{name}"] = chk
h = hashlib.sha256()
for name in sorted(merged_checksums):
h.update(name.encode())
h.update(merged_checksums[name].encode())
target["checksums"] = merged_checksums
target["per_gpu_checksum"] = h.hexdigest()
return target
@dataclass(kw_only=True, slots=True)
class SchedulerWeightUpdaterManager:
tp_worker: Any
draft_worker: Any
tp_cpu_group: Any
memory_saver_adapter: Any
flush_cache: Callable[..., bool]
is_fully_idle: Callable[..., bool]
scheduler: Optional[Any] = None
metrics_collector: Optional[Any] = None
offload_tags: set = field(default_factory=set)
stashed_model_static_state: Any = None
@contextmanager
def _observe_weight_load(self, source: str) -> Iterator[None]:
# Edge-trigger weight_load_duration_seconds at the end of each
# update_weights_from_* call. Engine is paused during the update so
# the periodic log_stats path can't carry this.
# `source` distinguishes disk vs distributed vs tensor vs ipc.
t0 = time.perf_counter()
try:
yield
finally:
if self.metrics_collector is not None:
self.metrics_collector.observe_weight_load(
time.perf_counter() - t0, source
)
def flush_cache_after_weight_update(self, recv_req) -> None:
if recv_req.flush_cache:
flush_cache_success = self.flush_cache(
empty_cache=recv_req.torch_empty_cache
)
assert flush_cache_success, "Cache flush failed after updating weights"
def update_weights_from_disk(self, recv_req: UpdateWeightFromDiskReqInput):
"""In-place update of the weights from disk."""
with self._observe_weight_load("disk"):
success, message = self.tp_worker.update_weights_from_disk(recv_req)
tp_success = success
if success and self.draft_worker is not None:
success, message = self.draft_worker.update_weights_from_disk(recv_req)
if tp_success:
self.flush_cache_after_weight_update(recv_req)
if not success:
logger.error(message)
return UpdateWeightFromDiskReqOutput(
success=success, message=message, num_paused_requests=0
)
def init_weights_update_group(self, recv_req: InitWeightsUpdateGroupReqInput):
"""Initialize the online model parameter update group."""
success, message = self.tp_worker.init_weights_update_group(recv_req)
return InitWeightsUpdateGroupReqOutput(success=success, message=message)
def destroy_weights_update_group(
self,
recv_req: DestroyWeightsUpdateGroupReqInput,
):
"""Destroy the online model parameter update group."""
success, message = self.tp_worker.destroy_weights_update_group(recv_req)
return DestroyWeightsUpdateGroupReqOutput(success=success, message=message)
def update_weights_from_distributed(
self,
recv_req: UpdateWeightsFromDistributedReqInput,
) -> Tuple[bool, str]:
"""Update the online model parameter."""
with self._observe_weight_load("distributed"):
success, message = self.tp_worker.update_weights_from_distributed(recv_req)
if success:
self.flush_cache_after_weight_update(recv_req)
else:
logger.error(message)
return UpdateWeightsFromDistributedReqOutput(
success=success, message=message
)
def update_weights_from_tensor(self, recv_req: UpdateWeightsFromTensorReqInput):
"""Update the online model parameter from tensors."""
with self._observe_weight_load("tensor"):
if recv_req.disable_draft_model:
worker = self.tp_worker
else:
worker = self.draft_worker or self.tp_worker
success, message = worker.update_weights_from_tensor(recv_req)
if success:
self.flush_cache_after_weight_update(recv_req)
else:
logger.error(message)
torch.distributed.barrier(group=self.tp_cpu_group)
return UpdateWeightsFromTensorReqOutput(success=success, message=message)
def update_weights_from_ipc(self, recv_req: UpdateWeightsFromIPCReqInput):
"""Update the online model parameter from IPC for checkpoint-engine integration."""
with self._observe_weight_load("ipc"):
success, message = self.tp_worker.update_weights_from_ipc(recv_req)
tp_success = success
if success and self.draft_worker is not None:
success, message = self.draft_worker.update_weights_from_ipc(recv_req)
if tp_success:
self.flush_cache_after_weight_update(recv_req)
if not success:
logger.error(message)
torch.distributed.barrier(group=self.tp_cpu_group)
return UpdateWeightsFromIPCReqOutput(success=success, message=message)
def get_weights_by_name(self, recv_req: GetWeightsByNameReqInput):
parameter = self.tp_worker.get_weights_by_name(recv_req)
return GetWeightsByNameReqOutput(parameter=parameter)
def release_memory_occupation(self, recv_req: ReleaseMemoryOccupationReqInput):
assert (
self.is_fully_idle()
), "release_memory_occupation should be called only when server is idle."
tags = recv_req.tags
if tags is None or len(tags) == 0:
tags = GPU_MEMORY_ALL_TYPES
for tag in tags:
self.offload_tags.add(tag)
if GPU_MEMORY_TYPE_KV_CACHE in tags:
scheduler = self.scheduler
if scheduler is not None:
if scheduler.disaggregation_mode == DisaggregationMode.DECODE:
for queue_name in (
"disagg_decode_transfer_queue",
"disagg_decode_prealloc_queue",
):
queue = getattr(scheduler, queue_name, None)
if queue is not None:
queue.release_memory_occupation()
elif scheduler.disaggregation_mode == DisaggregationMode.PREFILL:
queue = getattr(scheduler, "disagg_prefill_bootstrap_queue", None)
if queue is not None:
queue.release_memory_occupation()
self.memory_saver_adapter.pause(GPU_MEMORY_TYPE_KV_CACHE)
self.flush_cache()
if GPU_MEMORY_TYPE_WEIGHTS in tags:
self.stashed_model_static_state = _export_static_state(
self.tp_worker.model_runner.model
)
torch.distributed.barrier(self.tp_cpu_group)
self.memory_saver_adapter.pause(GPU_MEMORY_TYPE_WEIGHTS)
if GPU_MEMORY_TYPE_CUDA_GRAPH in tags:
self.memory_saver_adapter.pause(GPU_MEMORY_TYPE_CUDA_GRAPH)
torch.get_device_module().synchronize()
return ReleaseMemoryOccupationReqOutput()
def resume_memory_occupation(self, recv_req: ResumeMemoryOccupationReqInput):
tags = recv_req.tags
if tags is None or len(tags) == 0:
tags = GPU_MEMORY_ALL_TYPES
for tag in tags:
self.offload_tags.remove(tag)
if GPU_MEMORY_TYPE_CUDA_GRAPH in tags:
self.memory_saver_adapter.resume(GPU_MEMORY_TYPE_CUDA_GRAPH)
if GPU_MEMORY_TYPE_WEIGHTS in tags:
self.memory_saver_adapter.resume(GPU_MEMORY_TYPE_WEIGHTS)
torch.distributed.barrier(self.tp_cpu_group)
_import_static_state(
self.tp_worker.model_runner.model,
self.stashed_model_static_state,
)
del self.stashed_model_static_state
if GPU_MEMORY_TYPE_KV_CACHE in tags:
self.memory_saver_adapter.resume(GPU_MEMORY_TYPE_KV_CACHE)
scheduler = self.scheduler
if scheduler is not None:
if scheduler.disaggregation_mode == DisaggregationMode.DECODE:
for queue_name in (
"disagg_decode_transfer_queue",
"disagg_decode_prealloc_queue",
):
queue = getattr(scheduler, queue_name, None)
if queue is not None:
queue.resume_memory_occupation()
elif scheduler.disaggregation_mode == DisaggregationMode.PREFILL:
queue = getattr(scheduler, "disagg_prefill_bootstrap_queue", None)
if queue is not None:
queue.resume_memory_occupation()
return ResumeMemoryOccupationReqOutput()
def check_weights(self, recv_req: CheckWeightsReqInput):
try:
payload = self.tp_worker.model_runner.check_weights(
action=recv_req.action, allow_quant_error=recv_req.allow_quant_error
)
if self.draft_worker is not None:
draft_runner = _get_draft_model_runner(self.draft_worker)
if draft_runner is not None:
draft_payload = draft_runner.check_weights(
action=recv_req.action,
allow_quant_error=recv_req.allow_quant_error,
)
if payload is not None and draft_payload is not None:
payload = _merge_checksum_payloads(payload, draft_payload)
tp_size = torch.distributed.get_world_size(group=self.tp_cpu_group)
if tp_size > 1 and payload is not None:
all_payloads = [None] * tp_size
torch.distributed.all_gather_object(
all_payloads, payload, group=self.tp_cpu_group
)
payload = all_payloads
return CheckWeightsReqOutput(
success=True, message="Success.", payload=payload
)
except Exception as e:
logger.warning(f"check_weights see error: {e}")
traceback.print_exc()
return CheckWeightsReqOutput(success=False, message=f"{e}")
def save_remote_model(self, params):
url = params["url"]
self.tp_worker.model_runner.save_remote_model(url)
if self.draft_worker is not None:
draft_url = params.get("draft_url", None)
assert (
draft_url is not None
), "draft_url must be provided when draft model is enabled"
self.draft_worker.model_runner.save_remote_model(draft_url)
def save_sharded_model(self, params):
self.tp_worker.model_runner.save_sharded_model(
path=params["path"],
pattern=params["pattern"],
max_size=params["max_size"],
)
def _export_static_state(model):
return dict(
buffers=[
(name, buffer.detach().clone()) for name, buffer in model.named_buffers()
]
)
def _import_static_state(model, static_params):
with torch.inference_mode():
self_named_buffers = dict(model.named_buffers())
for name, tensor in static_params["buffers"]:
self_named_buffers[name][...] = tensor