Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/pr-test-xpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ jobs:
timeout-minutes: 60
run: |
docker exec ci_sglang_xpu /opt/venv/bin/python3 -m pip install --upgrade pip
docker exec ci_sglang_xpu /opt/venv/bin/python3 -m pip install pytest expecttest ray huggingface_hub tabulate "lmcache>=0.3.9"
docker exec ci_sglang_xpu /opt/venv/bin/python3 -m pip install pytest expecttest ray huggingface_hub tabulate "lmcache>=0.3.9" accelerate
docker exec ci_sglang_xpu /opt/venv/bin/python3 -m pip uninstall -y flashinfer-python sgl-kernel sglang
docker exec ci_sglang_xpu cp /sglang-checkout/python/pyproject_xpu.toml /sglang-checkout/python/pyproject.toml
# Fetch tags so setuptools_scm resolves a real version instead of
Expand Down Expand Up @@ -202,7 +202,7 @@ jobs:
timeout-minutes: 60
run: |
docker exec ci_sglang_xpu /opt/venv/bin/python3 -m pip install --upgrade pip
docker exec ci_sglang_xpu /opt/venv/bin/python3 -m pip install pytest expecttest ray huggingface_hub tabulate "lmcache>=0.3.9"
docker exec ci_sglang_xpu /opt/venv/bin/python3 -m pip install pytest expecttest ray huggingface_hub tabulate "lmcache>=0.3.9" accelerate
docker exec ci_sglang_xpu /opt/venv/bin/python3 -m pip uninstall -y flashinfer-python sgl-kernel sglang
docker exec ci_sglang_xpu cp /sglang-checkout/python/pyproject_xpu.toml /sglang-checkout/python/pyproject.toml
# Fetch tags so setuptools_scm resolves a real version instead of
Expand Down
2 changes: 2 additions & 0 deletions python/sglang/srt/arg_groups/overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,8 @@ def _step3p_overrides(server_args: Any, hf_config: Any) -> dict:
def supports_mamba_cache_extra_buffer(view: Any, model_arch: str) -> bool:
"""Whether ``model_arch`` supports the extra_buffer strategy on the
configured linear-attention backend (pure read)."""
if get_platform().is_xpu:
return False
if model_arch in _MAMBA_EXTRA_BUFFER_ARCHS:
return view.linear_attn_backend == "triton"
return False
Expand Down
16 changes: 14 additions & 2 deletions python/sglang/srt/layers/rotary_embedding/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,15 @@
)

if _is_xpu:
from sgl_kernel import fused_qk_rope_with_cos_sin_cache_inplace
try:
from sgl_kernel import fused_qk_rope_with_cos_sin_cache_inplace
except ImportError:
fused_qk_rope_with_cos_sin_cache_inplace = None
logger.warning(
"sgl_kernel.fused_qk_rope_with_cos_sin_cache_inplace is unavailable; "
"XPU rotary embedding will use the generic rotary_embedding kernel. "
"Upgrade sgl_kernel to enable the fused XPU kernel."
)


class RotaryEmbedding(BaseFusedOp):
Expand Down Expand Up @@ -454,7 +462,11 @@ def forward_xpu(
positions = torch.add(positions, offsets) if offsets is not None else positions

# Fused_qk_rope only supports aligned head_size
if self.head_size in [128, 256, 512]:
if fused_qk_rope_with_cos_sin_cache_inplace is not None and self.head_size in [
128,
256,
512,
]:
num_tokens = positions.size(0)
q_rope = query.view(num_tokens, -1, self.head_size)
k_rope = key.view(num_tokens, -1, self.head_size)
Expand Down
11 changes: 9 additions & 2 deletions python/sglang/srt/layers/rotary_embedding/mrope.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@
import torch_npu

if _is_xpu:
from sgl_kernel import multimodal_rotary_embedding
try:
from sgl_kernel import multimodal_rotary_embedding
except ImportError:
multimodal_rotary_embedding = None

from sglang.kernels.ops.attention.mrope import apply_interleaved_rope_triton

Expand Down Expand Up @@ -309,7 +312,11 @@ def forward_xpu(
) -> Tuple[torch.Tensor, torch.Tensor]:
assert positions.ndim in (1, 2)
self._match_cos_sin_cache_dtype(query)
if positions.ndim == 2 and self.mrope_section:
if (
multimodal_rotary_embedding is not None
and positions.ndim == 2
and self.mrope_section
):
multimodal_rotary_embedding(
query,
key,
Expand Down
10 changes: 6 additions & 4 deletions python/sglang/srt/lora/backend/base_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,17 +440,19 @@ def _compute_moe_lora_info(
adapter_enabled.zero_()

has_segments = weight_indices.numel() != 0
use_cuda_kernel = (
num_tokens != 0 and has_segments and seg_indptr.device.type == "cuda"
)
if use_cuda_kernel:
needs_launch = num_tokens != 0 and has_segments
if needs_launch:
block_size = 256
tiles_per_segment = triton.cdiv(max_len, block_size)
grid_size = tiles_per_segment * weight_indices.numel()
assert grid_size * block_size >= num_tokens, (
f"MoE LoRA token-mapping launch under-covers tokens: "
f"{grid_size=} {block_size=} {num_tokens=}"
)

# Triton kernel on CUDA only; every other device (e.g. XPU) falls through to
# the native torch path below, which yields the same mapping.
if needs_launch and seg_indptr.device.type == "cuda":
_compute_moe_lora_info_kernel[(grid_size,)](
seg_indptr,
lora_ranks,
Expand Down
2 changes: 1 addition & 1 deletion python/sglang/srt/lora/backend/chunked_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ def init_cuda_graph_batch_info(
(num_tokens_per_req + MIN_CHUNK_SIZE - 1) // MIN_CHUNK_SIZE
) * max_bs_in_cuda_graph
max_num_tokens = max_bs_in_cuda_graph * num_tokens_per_req
with torch.device("cuda"):
with torch.device(self.device):
self.cuda_graph_batch_info = LoRABatchInfo(
bs=max_bs_in_cuda_graph,
use_cuda_graph=True,
Expand Down
2 changes: 1 addition & 1 deletion python/sglang/srt/lora/backend/torch_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def init_cuda_graph_batch_info(
max_bs_in_cuda_graph: int,
num_tokens_per_req: int,
):
with torch.device("cuda"):
with torch.device(self.device):
self.cuda_graph_batch_info = TorchNativeLoRABatchInfo(
use_cuda_graph=True,
bs=max_bs_in_cuda_graph,
Expand Down
2 changes: 1 addition & 1 deletion python/sglang/srt/lora/backend/triton_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def init_cuda_graph_batch_info(
):
max_tokens = max_bs_in_cuda_graph * num_tokens_per_req
mlpb = self.max_loras_per_batch
with torch.device("cuda"):
with torch.device(self.device):
self.cuda_graph_batch_info = LoRABatchInfo(
bs=max_bs_in_cuda_graph,
use_cuda_graph=True,
Expand Down
2 changes: 1 addition & 1 deletion python/sglang/srt/lora/lora_moe_runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ def _compute_lora_alignment(

device = topk_ids.device

use_naive = (
use_naive = _is_xpu or (
cg is None
and M * topk_ids.shape[1] * _SPARSITY_FACTOR
<= lora_info.num_experts * max_loras
Expand Down
19 changes: 11 additions & 8 deletions python/sglang/srt/lora/lora_overlap_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,18 @@ def try_overlap_load_lora(
lora_pipeline_load_status = self._check_overlap_load_status(lora_id)
if lora_pipeline_load_status == LoRAOverlapLoadStatus.LOADING:
return False
elif lora_pipeline_load_status == LoRAOverlapLoadStatus.NOT_LOADED:
res = self._try_start_overlap_load(lora_id, running_loras)
if res:
logger.debug(f"Loading LoRA adapter {lora_id} asynchronously")
elif lora_pipeline_load_status == LoRAOverlapLoadStatus.LOADED:
return True

assert lora_pipeline_load_status == LoRAOverlapLoadStatus.NOT_LOADED
if not self._try_start_overlap_load(lora_id, running_loras):
return False
else:
assert lora_pipeline_load_status == LoRAOverlapLoadStatus.LOADED
return True
logger.debug(f"Loading LoRA adapter {lora_id} asynchronously")

# Report an already-finished copy as LOADED, or a sibling's load could
# evict it before it is ever scheduled. No-op while still in flight.
self._drain_completed_overlap_loads()
return self._check_overlap_load_status(lora_id) == LoRAOverlapLoadStatus.LOADED

def _check_overlap_load_status(
self, lora_id: Optional[str]
Expand All @@ -74,7 +77,7 @@ def _drain_completed_overlap_loads(self) -> None:
if event.query()
]
for lora_id, event in completed_loads:
torch.cuda.current_stream().wait_event(event)
self.device_module.current_stream().wait_event(event)
del self.lora_to_overlap_load_event[lora_id]

def _try_start_overlap_load(
Expand Down
52 changes: 42 additions & 10 deletions python/sglang/test/lora_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,36 @@

import torch

from sglang.srt.utils import is_xpu
from sglang.test.runners import HFRunner, SRTRunner
from sglang.test.test_utils import calculate_rouge_l

_IS_XPU = is_xpu()


def _assert_lora_output_match(
srt_str: str, hf_str: str, rouge_tol: float, context: str
):
"""Compare SRT vs HF greedy output strings.

Everywhere except XPU we keep the historical strict exact-match (SGLang and HF
kernels agree numerically enough for greedy argmax to pick identical tokens).
On XPU, small kernel-level fp differences can make greedy decoding diverge
after a shared prefix even when the LoRA math is correct, so we fall back to
the same ROUGE-L tolerance the per-adaptor comparison path uses.
"""
srt_str = srt_str.strip(" ")
hf_str = hf_str.strip(" ")
if not _IS_XPU:
assert srt_str == hf_str, (srt_str, hf_str)
return
rouge_score = calculate_rouge_l([srt_str], [hf_str])[0]
if rouge_score < rouge_tol:
raise AssertionError(
f"ROUGE-L score {rouge_score} below tolerance {rouge_tol} for {context}. "
f"SRT: {srt_str!r} HF: {hf_str!r}"
)


@dataclasses.dataclass
class LoRAAdaptor:
Expand Down Expand Up @@ -624,17 +651,22 @@ def run_lora_test_by_batch(
print("HF output:", hf_output_str)
print("SRT no lora output:", srt_no_lora_outputs.output_strs[i].strip())
print("HF no lora output:", hf_no_lora_outputs.output_strs[i].strip())
assert srt_outputs.output_strs[i].strip(" ") == hf_outputs.output_strs[i].strip(
" "
), (
srt_outputs.output_strs[i].strip(" "),
hf_outputs.output_strs[i].strip(" "),
rouge_tol = (
adaptors[i].rouge_l_tolerance
if adaptors[i].rouge_l_tolerance is not None
else model_case.rouge_l_tolerance
)
_assert_lora_output_match(
srt_outputs.output_strs[i],
hf_outputs.output_strs[i],
rouge_tol,
f"base '{base_path}', adaptor '{adaptor_names[i]}', backend '{backend}' (LoRA)",
)
assert srt_no_lora_outputs.output_strs[i].strip(
" "
) == hf_no_lora_outputs.output_strs[i].strip(" "), (
srt_no_lora_outputs.output_strs[i].strip(" "),
hf_no_lora_outputs.output_strs[i].strip(" "),
_assert_lora_output_match(
srt_no_lora_outputs.output_strs[i],
hf_no_lora_outputs.output_strs[i],
rouge_tol,
f"base '{base_path}', backend '{backend}' (no-LoRA baseline)",
)


Expand Down
Loading
Loading