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
197 lines
7.1 KiB
Python
197 lines
7.1 KiB
Python
"""Primitive RoPE ops: rotate helpers and apply_rotary_emb utilities."""
|
|
|
|
from typing import Optional, Tuple
|
|
|
|
import torch
|
|
|
|
from sglang.jit_kernel.diffusion.triton.rotary import apply_rotary_embedding
|
|
from sglang.kernel_api_logging import debug_kernel_api
|
|
from sglang.multimodal_gen.runtime.platforms import current_platform
|
|
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
|
|
from sglang.srt.utils.custom_op import register_custom_op_from_extern
|
|
|
|
logger = init_logger(__name__)
|
|
|
|
_is_cuda = current_platform.is_cuda()
|
|
if _is_cuda:
|
|
try:
|
|
from flashinfer.rope import (
|
|
apply_rope_with_cos_sin_cache_inplace as _flashinfer_apply_rope_inplace,
|
|
)
|
|
except Exception:
|
|
_flashinfer_apply_rope_inplace = None
|
|
else:
|
|
_flashinfer_apply_rope_inplace = None
|
|
|
|
if _flashinfer_apply_rope_inplace is not None:
|
|
flashinfer_apply_rope_inplace = register_custom_op_from_extern(
|
|
_flashinfer_apply_rope_inplace,
|
|
op_name="flashinfer_apply_rope_with_cos_sin_cache_inplace",
|
|
mutates_args=["query", "key"],
|
|
)
|
|
else:
|
|
flashinfer_apply_rope_inplace = None
|
|
|
|
|
|
def _apply_rotary_emb(
|
|
x: torch.Tensor,
|
|
cos: torch.Tensor,
|
|
sin: torch.Tensor,
|
|
is_neox_style: bool,
|
|
interleaved: bool = False,
|
|
) -> torch.Tensor:
|
|
"""
|
|
Args:
|
|
x: [num_tokens, num_heads, head_size] or [num_tokens, head_size]
|
|
cos: [num_tokens, head_size // 2]
|
|
sin: [num_tokens, head_size // 2]
|
|
is_neox_style: Whether to use the Neox-style or GPT-J-style rotary
|
|
positional embeddings.
|
|
"""
|
|
# cos = cos.unsqueeze(-2).to(x.dtype)
|
|
# sin = sin.unsqueeze(-2).to(x.dtype)
|
|
if is_neox_style:
|
|
cos = cos.unsqueeze(-2)
|
|
sin = sin.unsqueeze(-2)
|
|
if is_neox_style:
|
|
x1, x2 = torch.chunk(x, 2, dim=-1)
|
|
else:
|
|
x1 = x[..., ::2]
|
|
x2 = x[..., 1::2]
|
|
o1 = (x1.float() * cos - x2.float() * sin).type_as(x)
|
|
o2 = (x2.float() * cos + x1.float() * sin).type_as(x)
|
|
return torch.cat((o1, o2), dim=-1)
|
|
else:
|
|
return apply_rotary_embedding(x, cos, sin, interleaved)
|
|
|
|
|
|
@debug_kernel_api
|
|
def apply_flashinfer_rope_qk_inplace(
|
|
q: torch.Tensor,
|
|
k: torch.Tensor,
|
|
cos_sin_cache: torch.Tensor,
|
|
*,
|
|
head_size: Optional[int] = None,
|
|
is_neox: bool = False,
|
|
positions: Optional[torch.Tensor] = None,
|
|
) -> Tuple[torch.Tensor, torch.Tensor]:
|
|
if q.dim() != 4 or k.dim() != 4:
|
|
raise ValueError(
|
|
f"Expected q/k to be 4D [bsz, seqlen, nheads, head_size], "
|
|
f"got q:{tuple(q.shape)} k:{tuple(k.shape)}"
|
|
)
|
|
if q.shape[:2] != k.shape[:2] or q.shape[-1] != k.shape[-1]:
|
|
raise ValueError(
|
|
f"q and k must share batch, sequence, and head size, got {q.shape} vs {k.shape}"
|
|
)
|
|
|
|
if not (isinstance(cos_sin_cache, torch.Tensor) and cos_sin_cache.dim() == 2):
|
|
raise ValueError("cos_sin_cache must be a 2D torch.Tensor")
|
|
|
|
bsz, seqlen, q_heads, d = q.shape
|
|
k_heads = k.shape[2]
|
|
rope_dim = cos_sin_cache.shape[-1]
|
|
if k.device != q.device or cos_sin_cache.device != q.device:
|
|
raise ValueError(
|
|
"q, k, and cos_sin_cache must be on the same device, "
|
|
f"got q={q.device}, k={k.device}, cos_sin_cache={cos_sin_cache.device}"
|
|
)
|
|
if rope_dim % 2 != 0 or rope_dim > d:
|
|
raise ValueError(
|
|
f"cos_sin_cache width must be even and <= head_size, got {rope_dim} vs {d}"
|
|
)
|
|
if head_size is None:
|
|
head_size = d
|
|
if head_size != d:
|
|
raise ValueError(f"head_size mismatch: inferred {d}, but head_size={head_size}")
|
|
|
|
use_flashinfer = (
|
|
flashinfer_apply_rope_inplace is not None
|
|
and q.is_cuda
|
|
and k.is_cuda
|
|
and cos_sin_cache.is_cuda
|
|
and q_heads == k_heads
|
|
)
|
|
|
|
if not use_flashinfer:
|
|
if flashinfer_apply_rope_inplace is None:
|
|
_warn_about_missing_flashinfer()
|
|
|
|
half_size = rope_dim // 2
|
|
if positions is None:
|
|
cos = cos_sin_cache[:seqlen, :half_size].to(q.dtype)
|
|
sin = cos_sin_cache[:seqlen, half_size:].to(q.dtype)
|
|
cos = cos.unsqueeze(0).expand(bsz, -1, -1).reshape(bsz * seqlen, -1)
|
|
sin = sin.unsqueeze(0).expand(bsz, -1, -1).reshape(bsz * seqlen, -1)
|
|
else:
|
|
positions = positions.to(device=q.device, dtype=torch.long).view(-1)
|
|
cos = cos_sin_cache[positions, :half_size].to(q.dtype)
|
|
sin = cos_sin_cache[positions, half_size:].to(q.dtype)
|
|
|
|
if current_platform.is_npu():
|
|
q_flat = q.reshape(bsz * seqlen, q_heads, d)
|
|
k_flat = k.reshape(bsz * seqlen, k_heads, d)
|
|
q_rot = apply_rotary_embedding(q_flat, cos, sin, interleaved=not is_neox)
|
|
k_rot = apply_rotary_embedding(k_flat, cos, sin, interleaved=not is_neox)
|
|
return q_rot.view(bsz, seqlen, q_heads, d), k_rot.view(
|
|
bsz, seqlen, k_heads, d
|
|
)
|
|
|
|
def apply_rope_prefix(x: torch.Tensor, num_heads: int) -> torch.Tensor:
|
|
x_flat = x.reshape(bsz * seqlen, num_heads, d)
|
|
x_rot = x_flat[..., :rope_dim]
|
|
out_rot = torch.empty_like(x_rot)
|
|
cos_b = cos.unsqueeze(-2)
|
|
sin_b = sin.unsqueeze(-2)
|
|
if is_neox:
|
|
x1, x2 = torch.chunk(x_rot, 2, dim=-1)
|
|
out_rot[..., :half_size] = x1 * cos_b - x2 * sin_b
|
|
out_rot[..., half_size:] = x2 * cos_b + x1 * sin_b
|
|
else:
|
|
x1 = x_rot[..., ::2]
|
|
x2 = x_rot[..., 1::2]
|
|
out_rot[..., ::2] = x1 * cos_b - x2 * sin_b
|
|
out_rot[..., 1::2] = x2 * cos_b + x1 * sin_b
|
|
if rope_dim == d:
|
|
return out_rot.view(bsz, seqlen, num_heads, d)
|
|
out = x_flat.clone()
|
|
out[..., :rope_dim] = out_rot
|
|
return out.view(bsz, seqlen, num_heads, d)
|
|
|
|
return apply_rope_prefix(q, q_heads), apply_rope_prefix(k, k_heads)
|
|
|
|
if positions is None:
|
|
pos_1d = torch.arange(seqlen, device=q.device, dtype=torch.long)
|
|
positions = pos_1d if bsz == 1 else pos_1d.repeat(bsz)
|
|
else:
|
|
if not (isinstance(positions, torch.Tensor) and positions.dim() == 1):
|
|
raise ValueError("positions must be a 1D Tensor")
|
|
if positions.numel() != bsz * seqlen:
|
|
raise ValueError(
|
|
f"positions length must be bsz*seqlen={bsz*seqlen}, got {positions.numel()}"
|
|
)
|
|
positions = positions.to(device=q.device, dtype=torch.long)
|
|
|
|
q_flat = q.reshape(bsz * seqlen, q_heads * d).contiguous()
|
|
k_flat = k.reshape(bsz * seqlen, k_heads * d).contiguous()
|
|
flashinfer_apply_rope_inplace(
|
|
positions=positions,
|
|
query=q_flat,
|
|
key=k_flat,
|
|
head_size=d,
|
|
cos_sin_cache=cos_sin_cache,
|
|
is_neox=is_neox,
|
|
)
|
|
return q_flat.view(bsz, seqlen, q_heads, d), k_flat.view(bsz, seqlen, k_heads, d)
|
|
|
|
|
|
@torch.compiler.assume_constant_result
|
|
def _warn_about_missing_flashinfer():
|
|
"""
|
|
Function to warn about the missing FlashInfer.
|
|
Exists to not cause a graph break during the compilation.
|
|
"""
|
|
logger.warning_once(
|
|
"FlashInfer not available, using Triton fallback for RoPE",
|
|
)
|