Files
sgl-project--sglang/python/sglang/srt/layers/attention/dsv4/dequant_k_cache.py
T
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

227 lines
7.8 KiB
Python

from typing import Optional
import torch
import triton
import triton.language as tl
from sglang.srt.layers.quantization.fp8_kernel import is_fp8_fnuz
fp8_dtype = torch.float8_e4m3fnuz if is_fp8_fnuz() else torch.float8_e4m3fn
# v4 KV cache layout (see dsv4.index_buf_accessor._set_k_and_s_triton_kernel):
# per-token: 448 fp8 nope + 64 bf16 rope (= 576 contiguous bytes) +
# 7 ue8m0 scales padded to 8 bytes.
# per-page: [token 0..P-1 nope+rope (P*576 bytes)] [token 0..P-1 scale (P*8 bytes)]
# padded up to a multiple of 576.
DIM_NOPE = 448
DIM_ROPE = 64
TILE_SIZE = 64 # one nope scale tile = 64 fp8 values
NUM_SCALE_TILES = DIM_NOPE // TILE_SIZE # 7
NOPE_ROPE_BYTES = DIM_NOPE + DIM_ROPE * 2 # 576
PADDED_SCALE_PER_TOKEN = NUM_SCALE_TILES + 1 # 8
def dequantize_k_cache_paged(
quant_k_cache: torch.Tensor,
page_table_1_flattened: torch.Tensor,
page_size: int,
out: Optional[torch.Tensor] = None,
) -> torch.Tensor:
"""Dequantize the DeepSeek v4 paged KV cache for a list of token IDs.
Args:
quant_k_cache: (num_pages, bytes_per_page_padded) uint8.
page_table_1_flattened: (num_tokens,) int — token IDs into the cache.
page_size: number of tokens per page.
out: optional (num_tokens, 1, DIM_NOPE + DIM_ROPE) bf16 destination.
May be a slice of a larger workspace; the kernel uses out.stride(0)
so contiguous-along-dim-0 slices work.
Returns:
(num_tokens, 1, DIM_NOPE + DIM_ROPE) bfloat16.
"""
assert quant_k_cache.is_contiguous()
assert page_table_1_flattened.dtype in (torch.int32, torch.int64)
# The buffer's dtype is whatever the pool exposes (often bf16); the
# underlying storage is uint8. Reinterpret to byte-space first.
quant_k_cache_u8 = quant_k_cache.view(torch.uint8)
num_tokens = page_table_1_flattened.shape[0]
bytes_per_page = quant_k_cache_u8.shape[-1]
s_offset_bytes = page_size * NOPE_ROPE_BYTES
# Three typed views over the same underlying bytes.
buf_fp8 = quant_k_cache_u8.view(fp8_dtype).reshape(-1)
buf_bf16 = quant_k_cache_u8.view(torch.bfloat16).reshape(-1)
buf_uint8 = quant_k_cache_u8.reshape(-1)
if out is None:
out = torch.empty(
(num_tokens, 1, DIM_NOPE + DIM_ROPE),
dtype=torch.bfloat16,
device=quant_k_cache.device,
)
else:
assert out.shape == (num_tokens, 1, DIM_NOPE + DIM_ROPE)
assert out.dtype == torch.bfloat16
_dequantize_k_cache_paged_kernel[(num_tokens,)](
out,
buf_fp8,
buf_bf16,
buf_uint8,
page_table_1_flattened,
out.stride(0),
BYTES_PER_PAGE=bytes_per_page,
PAGE_SIZE=page_size,
DIM_NOPE=DIM_NOPE,
DIM_ROPE=DIM_ROPE,
TILE_SIZE=TILE_SIZE,
NUM_SCALE_TILES=NUM_SCALE_TILES,
NOPE_ROPE_BYTES=NOPE_ROPE_BYTES,
PADDED_SCALE_PER_TOKEN=PADDED_SCALE_PER_TOKEN,
S_OFFSET_BYTES=s_offset_bytes,
)
return out
@triton.jit
def _dequantize_k_cache_paged_kernel(
output_ptr,
buf_fp8_ptr,
buf_bf16_ptr,
buf_uint8_ptr,
page_table_ptr,
output_stride_0,
BYTES_PER_PAGE: tl.constexpr,
PAGE_SIZE: tl.constexpr,
DIM_NOPE: tl.constexpr,
DIM_ROPE: tl.constexpr,
TILE_SIZE: tl.constexpr,
NUM_SCALE_TILES: tl.constexpr,
NOPE_ROPE_BYTES: tl.constexpr,
PADDED_SCALE_PER_TOKEN: tl.constexpr,
S_OFFSET_BYTES: tl.constexpr,
):
# One program per token: load page_table[token_id] once and emit all
# NUM_SCALE_TILES nope tiles + rope tail via tl.static_range.
token_id = tl.program_id(0)
loc = tl.load(page_table_ptr + token_id).to(tl.int64)
page_idx = loc // PAGE_SIZE
in_page = loc % PAGE_SIZE
page_byte_base = page_idx * BYTES_PER_PAGE
token_data_base = page_byte_base + in_page * NOPE_ROPE_BYTES
token_scale_base = (
page_byte_base + S_OFFSET_BYTES + in_page * PADDED_SCALE_PER_TOKEN
)
out_row_base = token_id * output_stride_0
nope_offs = tl.arange(0, TILE_SIZE)
for tile_id in tl.static_range(NUM_SCALE_TILES):
fp8_off = token_data_base + tile_id * TILE_SIZE + nope_offs
fp8_vals = tl.load(buf_fp8_ptr + fp8_off).to(tl.float32)
scale_u8 = tl.load(buf_uint8_ptr + token_scale_base + tile_id).to(tl.int32)
scale_pow2 = tl.exp2((scale_u8 - 127).to(tl.float32))
out_off = out_row_base + tile_id * TILE_SIZE + nope_offs
tl.store(
output_ptr + out_off,
(fp8_vals * scale_pow2).to(output_ptr.dtype.element_ty),
)
rope_offs = tl.arange(0, DIM_ROPE)
bf16_off = (token_data_base + DIM_NOPE) // 2 + rope_offs
rope_data = tl.load(buf_bf16_ptr + bf16_off)
tl.store(output_ptr + out_row_base + DIM_NOPE + rope_offs, rope_data)
def dequantize_k_cache_paged_ref(
quant_k_cache: torch.Tensor,
page_table_1_flattened: torch.Tensor,
page_size: int,
) -> torch.Tensor:
"""Pure-torch reference for :func:`dequantize_k_cache_paged`.
Decodes the same v4 paged layout with vectorized torch indexing instead of
a Triton kernel. Used to validate the kernel (see the ``__main__`` block
below); not on any hot path.
"""
assert page_table_1_flattened.dtype in (torch.int32, torch.int64)
u8 = quant_k_cache.view(torch.uint8)
bytes_per_page = u8.shape[-1]
s_offset_bytes = page_size * NOPE_ROPE_BYTES
flat_u8 = u8.reshape(-1)
flat_fp8 = u8.view(fp8_dtype).reshape(-1)
flat_bf16 = u8.view(torch.bfloat16).reshape(-1)
loc = page_table_1_flattened.to(torch.int64)
page_idx = loc // page_size
in_page = loc % page_size
page_byte_base = page_idx * bytes_per_page
token_data_base = page_byte_base + in_page * NOPE_ROPE_BYTES
token_scale_base = (
page_byte_base + s_offset_bytes + in_page * PADDED_SCALE_PER_TOKEN
)
device = quant_k_cache.device
nope_byte = (
token_data_base[:, None] + torch.arange(DIM_NOPE, device=device)[None, :]
)
nope_fp8 = flat_fp8[nope_byte].to(torch.float32)
scale_byte = (
token_scale_base[:, None]
+ torch.arange(NUM_SCALE_TILES, device=device)[None, :]
)
scale_u8 = flat_u8[scale_byte].to(torch.int32)
scale_pow2 = torch.exp2((scale_u8 - 127).to(torch.float32))
scale_pow2 = torch.where(
scale_pow2 < (2.0**-126), torch.zeros_like(scale_pow2), scale_pow2
)
scale_full = scale_pow2.repeat_interleave(TILE_SIZE, dim=1)
nope = nope_fp8 * scale_full
rope_bf16_base = (token_data_base + DIM_NOPE) // 2
rope_idx = rope_bf16_base[:, None] + torch.arange(DIM_ROPE, device=device)[None, :]
rope = flat_bf16[rope_idx]
out = torch.empty(
(loc.shape[0], 1, DIM_NOPE + DIM_ROPE),
dtype=torch.bfloat16,
device=device,
)
out[:, 0, :DIM_NOPE] = nope.to(torch.bfloat16)
out[:, 0, DIM_NOPE:] = rope
return out
if __name__ == "__main__":
assert torch.cuda.is_available(), "this self-test needs a CUDA device"
torch.manual_seed(0)
device = "cuda"
page_size = 64
num_pages = 8
num_tokens = 333
raw_bytes = page_size * (NOPE_ROPE_BYTES + PADDED_SCALE_PER_TOKEN)
bytes_per_page = (
(raw_bytes + NOPE_ROPE_BYTES - 1) // NOPE_ROPE_BYTES
) * NOPE_ROPE_BYTES
quant_k_cache = torch.randint(
0, 256, (num_pages, bytes_per_page), dtype=torch.uint8, device=device
)
page_table = torch.randint(
0, num_pages * page_size, (num_tokens,), dtype=torch.int32, device=device
)
out_kernel = dequantize_k_cache_paged(quant_k_cache, page_table, page_size)
out_ref = dequantize_k_cache_paged_ref(quant_k_cache, page_table, page_size)
torch.testing.assert_close(out_kernel, out_ref, atol=0, rtol=0, equal_nan=True)
print(
f"OK: kernel matches torch ref for {num_tokens} tokens "
f"(page_size={page_size}, bytes_per_page={bytes_per_page})"
)