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

568 lines
21 KiB
Python

from __future__ import annotations
import os
from functools import cached_property
from typing import TYPE_CHECKING, Any
import torch
import torch.nn as nn
import triton
import triton.language as tl
from sglang.srt.environ import envs
from sglang.srt.layers.attention.dsa.dsa_indexer import rotate_activation
from sglang.srt.layers.attention.dsv4.compressor import Compressor as _CompressorBase
from sglang.srt.layers.attention.dsv4.fused_compress_triton import (
fused_ape_pool_norm_rope,
)
from sglang.srt.layers.attention.nsa.nsa_indexer import rotate_activation
from sglang.srt.layers.deepseek_v4_rope import (
apply_rotary_emb_triton,
fused_norm_rope_inplace_triton,
fused_softmax_pool_triton,
)
try:
from sglang.srt.layers.deepseek_v4_rope import fused_softmax_pool_triton
except ImportError:
fused_softmax_pool_triton = None
from sglang.srt.mem_cache.deepseek_v4_compress_state import (
CompressStatePool,
KVAndScore,
)
from sglang.srt.mem_cache.deepseek_v4_memory_pool import DeepSeekV4TokenToKVPool
if TYPE_CHECKING:
from sglang.srt.layers.attention.base_attn_backend import AttentionBackend
from sglang.srt.layers.attention.deepseek_v4_backend_hip_radix import (
DeepseekV4HipRadixBackend,
)
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
@triton.jit
def _rms_normalize_kernel(
x_ptr,
weight_ptr,
eps,
stride_row,
dim,
BLOCK_SIZE: tl.constexpr,
HAS_WEIGHT: tl.constexpr,
):
pid = tl.program_id(0)
offs = tl.arange(0, BLOCK_SIZE)
mask = offs < dim
base = pid * stride_row
x = tl.load(x_ptr + base + offs, mask=mask, other=0.0).to(tl.float32)
mean_sq = tl.sum(x * x, axis=0) / dim
rms_inv = tl.rsqrt(mean_sq + eps)
out = x * rms_inv
if HAS_WEIGHT:
weight = tl.load(weight_ptr + offs, mask=mask, other=0.0)
out = out * weight
tl.store(x_ptr + base + offs, out, mask=mask)
def rms_normalize_triton(
x: torch.Tensor, eps: float, weight: torch.Tensor = None
) -> torch.Tensor:
dim = x.shape[-1]
x_flat = x.view(-1, dim)
num_rows = x_flat.shape[0]
BLOCK_SIZE = triton.next_power_of_2(dim)
grid = (num_rows,)
_rms_normalize_kernel[grid](
x_flat,
weight,
eps,
x_flat.stride(0),
dim,
BLOCK_SIZE=BLOCK_SIZE,
HAS_WEIGHT=(weight is not None),
)
return x
class DeepseekRefRMSNorm(nn.Module):
def __init__(self, dim: int, eps: float = 1e-6):
super().__init__()
self.dim = dim
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim, dtype=torch.float32))
def forward(self, x: torch.Tensor):
return rms_normalize_triton(x, self.eps, self.weight)
class CompressorHip(_CompressorBase):
"""HIP (ROCm) specific Compressor implementation."""
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.norm = DeepseekRefRMSNorm(self.head_dim, eps=self.norm.variance_epsilon)
self._freqs_cis_real: torch.Tensor | None = None
@cached_property
def use_fused_compress(self) -> bool:
return envs.SGLANG_OPT_USE_FUSED_COMPRESS.get()
@cached_property
def use_hip_fused_compress(self) -> bool:
return envs.SGLANG_OPT_USE_FUSED_COMPRESS.get()
@cached_property
def use_fused_compress_triton(self) -> bool:
# The fused Triton kernel only benefits non-overlap (HCA, ratio=128)
# but HCA's K=128 loop is too sequential to outperform batched ops.
# CSA (overlap=True) has a reshape/overlap-transform semantic mismatch.
# Disabled until a tiled kernel for CSA overlap is implemented.
return False
def _get_states(
self,
forward_batch: ForwardBatch,
attn_backend: AttentionBackend,
) -> KVAndScore:
token_to_kv_pool = attn_backend.token_to_kv_pool
assert isinstance(token_to_kv_pool, DeepSeekV4TokenToKVPool)
if self.is_in_indexer:
return token_to_kv_pool.get_indexer_compress_states(self.layer_id)
else:
return token_to_kv_pool.get_attention_compress_states(self.layer_id)
def _get_state_pool(self, attn_backend: AttentionBackend) -> CompressStatePool:
token_to_kv_pool = attn_backend.token_to_kv_pool
assert isinstance(token_to_kv_pool, DeepSeekV4TokenToKVPool)
if self.is_in_indexer:
ret = token_to_kv_pool.get_indexer_compress_states(self.layer_id)
else:
ret = token_to_kv_pool.get_attention_compress_states(self.layer_id)
assert isinstance(ret, CompressStatePool)
return ret
def overlap_transform(self, tensor: torch.Tensor, fill_value: Any) -> torch.Tensor:
assert tensor.dim() == 3
assert tensor.shape[1:] == (self.ratio, 2 * self.head_dim)
s, r, d = tensor.size(0), self.ratio, self.head_dim
new_tensor = tensor.new_full((s, 2 * r, d), fill_value)
new_tensor[:, r:] = tensor[:, :, d:]
new_tensor[1:, :r] = tensor[:-1, :, :d]
return new_tensor
def overlap_transform_decode(self, tensor: torch.Tensor) -> torch.Tensor:
assert tensor.dim() == 3
assert tensor.shape[1:] == (2 * self.ratio, 2 * self.head_dim)
r, d = self.ratio, self.head_dim
ret = torch.cat((tensor[:, :r, :d], tensor[:, r:, d:]), dim=1)
return ret
@staticmethod
def compute_state_len(seq_len: int, ratio: int):
return seq_len % ratio + (ratio == 4) * ratio
@staticmethod
def compute_state_len_indices(seq_len: int, ratio: int):
state_len = seq_len % ratio + (ratio == 4) * ratio
return torch.arange(seq_len - state_len, seq_len).clamp(min=-1)
def print_tensor(self, y: torch.Tensor, name: str):
enable = int(os.environ.get("SGLANG_ENABLE_PRINT_TENSOR", 0))
if enable:
print(f"[sgl] {name}: shape={y.shape}, dtype={y.dtype}, device={y.device}")
print(f"{y.flatten()[:10]}...{y.flatten()[-10:]}")
def compress_extend_paged(
self,
kv_and_scores: KVAndScore,
forward_batch: ForwardBatch,
attn_backend: AttentionBackend,
):
backend = attn_backend
if TYPE_CHECKING:
assert isinstance(backend, DeepseekV4HipRadixBackend)
token_to_kv_pool = backend.token_to_kv_pool
assert isinstance(token_to_kv_pool, DeepSeekV4TokenToKVPool)
state_pool = self._get_state_pool(backend)
prefix_lens = forward_batch.extend_prefix_lens_cpu
extend_lens = forward_batch.extend_seq_lens_cpu
req_pool_indices = forward_batch.req_pool_indices
req_to_token = backend.req_to_token_pool.req_to_token
assert not self.forward_mode.is_target_verify()
assert extend_lens is not None and prefix_lens is not None
device = kv_and_scores.kv.device
assert kv_and_scores.kv.shape[-1] == self.head_dim * self.coff
compressed_kv_output = torch.full(
(kv_and_scores.kv.size(0), self.head_dim),
fill_value=10000.0,
dtype=kv_and_scores.kv.dtype,
device=device,
)
bs = forward_batch.batch_size
pt = 0
for i in range(bs):
kv_and_score = kv_and_scores[pt : pt + extend_lens[i]]
pre_state_indices = self.compute_state_len_indices(
seq_len=prefix_lens[i], ratio=self.ratio
).to(device)
if self.ratio == 128:
state_loc = state_pool.translate_from_req_position_to_state_loc(
req_pool_indices[i], pre_state_indices
)
else:
raw_loc = torch.where(
pre_state_indices < 0,
-1,
req_to_token[req_pool_indices[i], pre_state_indices],
)
swa_loc = token_to_kv_pool.translate_loc_from_full_to_swa(raw_loc)
state_loc = state_pool.translate_from_swa_loc_to_state_loc(swa_loc)
pre_kv_state = state_pool.get_state_by_state_loc(state_loc)
kv_and_score_buffer = KVAndScore.cat([pre_kv_state, kv_and_score], dim=0)
valid_kv_len = kv_and_score_buffer.kv.size(0)
post_state_indices = self.compute_state_len_indices(
seq_len=prefix_lens[i] + extend_lens[i], ratio=self.ratio
).to(device)
post_state_len = post_state_indices.size(0)
assert post_state_len <= valid_kv_len
if self.ratio == 128:
post_state_loc = state_pool.translate_from_req_position_to_state_loc(
req_pool_indices[i], post_state_indices
)
else:
post_raw_loc = torch.where(
post_state_indices < 0,
-1,
req_to_token[req_pool_indices[i], post_state_indices],
)
post_swa_loc = token_to_kv_pool.translate_loc_from_full_to_swa(
post_raw_loc
)
post_state_loc = state_pool.translate_from_swa_loc_to_state_loc(
post_swa_loc
)
post_state_to_set = kv_and_score_buffer[valid_kv_len - post_state_len :]
state_pool.set_state_by_state_loc(post_state_loc, post_state_to_set)
compress_len = valid_kv_len // self.ratio * self.ratio
if compress_len == 0:
pt += extend_lens[i]
continue
kv_and_score_to_compress = kv_and_score_buffer[:compress_len].view(
compress_len // self.ratio, self.ratio, -1
)
kv_and_score_to_compress.score.add_(self.ape.unsqueeze(0))
if self.overlap:
new_kv = self.overlap_transform(
kv_and_score_to_compress.kv, fill_value=0
)
new_score = self.overlap_transform(
kv_and_score_to_compress.score, fill_value=float("-inf")
)
kv_and_score_to_compress = KVAndScore.from_kv_score(
kv=new_kv, score=new_score
)
del new_kv, new_score
kv_and_score_to_compress = kv_and_score_to_compress[1:]
if kv_and_score_to_compress.kv.size(0) == 0:
pt += extend_lens[i]
continue
beg_idx = prefix_lens[i] // self.ratio * self.ratio
end_idx = (prefix_lens[i] + extend_lens[i]) // self.ratio * self.ratio
if self.use_hip_fused_compress:
kv_compressed = fused_softmax_pool_triton(
kv_and_score_to_compress.kv_score,
kv_and_score_to_compress._item_size,
)
else:
kv_compressed = (
kv_and_score_to_compress.kv
* kv_and_score_to_compress.score.softmax(dim=1)
).sum(dim=1)
assert kv_compressed.dtype == torch.float32
freqs_cis = self.freqs_cis[beg_idx : end_idx : self.ratio]
assert freqs_cis.size(0) == kv_compressed.size(
0
), f"{freqs_cis.shape=} {kv_compressed.shape=}"
if self.use_hip_fused_compress:
fused_norm_rope_inplace_triton(
kv_compressed, self.norm.weight, self.norm.eps, freqs_cis
)
else:
kv_compressed = self.norm(kv_compressed)
apply_rotary_emb_triton(
kv_compressed[..., -self.rope_head_dim :], freqs_cis
)
del beg_idx, end_idx
if self.rotate:
kv_compressed = rotate_activation(kv_compressed)
start = prefix_lens[i]
start = start + self.ratio - 1 - start % self.ratio
indices_in_seq = torch.arange(
start,
prefix_lens[i] + extend_lens[i],
self.ratio,
device=kv_and_scores.kv.device,
)
assert indices_in_seq.size(0) == kv_compressed.size(0)
compressed_kv_output[indices_in_seq - prefix_lens[i] + pt] = kv_compressed
pt += extend_lens[i]
return compressed_kv_output
def compress_decode_paged(
self,
kv_and_scores: KVAndScore,
forward_batch: ForwardBatch,
attn_backend: AttentionBackend,
):
"""Paged and cudagraph compatible version of compress_decode"""
assert self.ape_converted
state_pool = self._get_state_pool(attn_backend)
token_to_kv_pool = attn_backend.token_to_kv_pool
assert isinstance(token_to_kv_pool, DeepSeekV4TokenToKVPool)
req_pool_indices = forward_batch.req_pool_indices
req_to_token = attn_backend.req_to_token_pool.req_to_token
seq_lens = forward_batch.seq_lens
if forward_batch.forward_mode.is_target_verify():
draft_tokens = attn_backend.speculative_num_draft_tokens
offsets = torch.arange(1, draft_tokens + 1, device=seq_lens.device)
seq_lens_2d = seq_lens[:, None] + offsets[None, :]
seq_lens = seq_lens_2d.view(-1)
req_pool_indices = req_pool_indices.repeat_interleave(draft_tokens)
if self.ratio == 128:
state_locs = state_pool.translate_from_req_position_to_state_loc(
req_pool_indices, seq_lens - 1
)
else:
raw_locs = req_to_token[req_pool_indices, seq_lens - 1]
swa_locs = token_to_kv_pool.translate_loc_from_full_to_swa(raw_locs)
state_locs = state_pool.translate_from_swa_loc_to_state_loc(swa_locs)
state_pool.set_state_by_state_loc(state_locs, kv_and_scores)
compress_bulk_len = self.ratio * self.coff
compress_indices = seq_lens[:, None] + torch.arange(
-compress_bulk_len, 0, device=seq_lens.device
)
compress_indices.clamp_(min=-1)
if self.ratio == 128:
compress_indices_state = (
state_pool.translate_from_req_position_to_state_loc(
req_pool_indices[:, None], compress_indices
)
)
else:
compress_indices_raw = torch.where(
compress_indices < 0,
-1,
req_to_token[req_pool_indices[:, None], compress_indices],
)
compress_indices_swa = token_to_kv_pool.translate_loc_from_full_to_swa(
compress_indices_raw
)
compress_indices_state = state_pool.translate_from_swa_loc_to_state_loc(
compress_indices_swa
)
kv_and_score_to_compress = state_pool.get_state_by_state_loc(
compress_indices_state.view(-1)
).view(-1, self.ratio, self.coff * self.head_dim)
bs = seq_lens.size(0)
if self.use_fused_compress_triton and not self.overlap:
# Fused path for non-overlap (HCA, ratio=128, coff=1):
# APE + softmax-pool + norm + RoPE in one kernel.
# Overlap (CSA) is excluded because the overlap_transform_decode
# rearranges A/B halves across the coff dimension in a way
# that simple reshape cannot replicate correctly.
raw = kv_and_score_to_compress.kv_score
gathered = raw.reshape(bs, self.ratio, raw.shape[-1]).contiguous()
comp_positions = (seq_lens - 1) // self.ratio * self.ratio
freqs_real_table = self._get_freqs_cis_real()
freqs_batch = freqs_real_table[comp_positions]
kv_compressed = fused_ape_pool_norm_rope(
kv_score_gathered=gathered,
ape=self.ape,
rms_weight=self.norm.weight,
rms_eps=self.norm.eps,
freqs_cis_real=freqs_batch,
head_dim=self.head_dim,
rope_head_dim=self.rope_head_dim,
ratio=self.ratio,
overlap=self.overlap,
)
if self.rotate:
kv_compressed = rotate_activation(kv_compressed)
return kv_compressed
# Unfused reference path
kv_and_score_to_compress.score.add_(self.ape.unsqueeze(0))
if self.overlap:
kv_and_score_to_compress = kv_and_score_to_compress.view(
bs, self.coff * self.ratio, self.coff * self.head_dim
)
kv_and_score_to_compress = KVAndScore.from_kv_score(
kv=self.overlap_transform_decode(kv_and_score_to_compress.kv),
score=self.overlap_transform_decode(kv_and_score_to_compress.score),
)
kv_and_score_to_compress = kv_and_score_to_compress.view(
bs, self.ratio * self.coff, self.head_dim
)
if self.use_hip_fused_compress:
kv_compressed = fused_softmax_pool_triton(
kv_and_score_to_compress.kv_score,
kv_and_score_to_compress._item_size,
)
else:
kv_compressed = (
kv_and_score_to_compress.kv
* kv_and_score_to_compress.score.softmax(dim=1)
).sum(dim=1)
if self.use_hip_fused_compress:
freqs_cis = self._init_freqs_cis_per_decode_step(forward_batch, seq_lens)
fused_norm_rope_inplace_triton(
kv_compressed, self.norm.weight, self.norm.eps, freqs_cis
)
else:
kv_compressed = self.norm(kv_compressed)
freqs_cis = self.freqs_cis[(seq_lens - 1) // self.ratio * self.ratio]
apply_rotary_emb_triton(
kv_compressed[..., -self.rope_head_dim :], freqs_cis
)
if self.rotate:
kv_compressed = rotate_activation(kv_compressed)
return kv_compressed
def compress_fused(
self,
kv_score: torch.Tensor,
forward_batch: ForwardBatch,
attn_backend: AttentionBackend,
) -> torch.Tensor:
backend = attn_backend
if TYPE_CHECKING:
assert isinstance(backend, DeepseekV4HipRadixBackend)
kv_score_buffer = self._get_state_pool(backend)
kv_score_buffer = kv_score_buffer.kv_score_buffer.kv_score
return backend.forward_compress(
kv_score_buffer=kv_score_buffer,
kv_score_input=kv_score,
ape=self.ape.view(-1, self.head_dim),
head_dim=self.head_dim,
norm=self.norm,
freqs_cis_cache=self.freqs_cis,
rotate=self.rotate,
compress_ratio=self.ratio,
forward_batch=forward_batch,
is_paged=True,
)
def _get_freqs_cis_real(self) -> torch.Tensor:
"""Cache the float32 view of freqs_cis (complex64 -> real interleaved)."""
if self._freqs_cis_real is None:
if self.freqs_cis.is_complex():
self._freqs_cis_real = (
torch.view_as_real(self.freqs_cis).flatten(-2).contiguous()
)
else:
self._freqs_cis_real = self.freqs_cis.contiguous()
return self._freqs_cis_real
def compress_dispatch(
self,
kv_score: torch.Tensor,
forward_batch: ForwardBatch,
attn_backend: AttentionBackend,
) -> torch.Tensor:
if self.use_fused_compress and (
envs.SGLANG_OPT_DPSK_V4_RADIX.get()
and (
forward_batch.forward_mode.is_decode()
or forward_batch.forward_mode.is_extend_without_speculative()
)
):
return self.compress_fused(
kv_score, forward_batch, attn_backend=attn_backend
)
self.compress_decode = self.compress_decode_paged
self.compress_extend = self.compress_extend_paged
kv_and_scores = KVAndScore(kv_score)
if TYPE_CHECKING:
assert isinstance(kv_and_scores, KVAndScore)
if (
forward_batch.forward_mode.is_decode()
or forward_batch.forward_mode.is_target_verify()
):
result = self.compress_decode(
kv_and_scores=kv_and_scores,
forward_batch=forward_batch,
attn_backend=attn_backend,
)
elif forward_batch.forward_mode.is_extend():
result = self.compress_extend(
kv_and_scores=kv_and_scores,
forward_batch=forward_batch,
attn_backend=attn_backend,
)
else:
msg = f"Forward mode {forward_batch.forward_mode} not supported in Compressor."
raise NotImplementedError(msg)
return result
def _init_freqs_cis_per_decode_step(
self,
forward_batch: ForwardBatch,
seq_lens: torch.Tensor,
) -> torch.Tensor:
attr = f"freqs_cis_c{self.ratio}"
cached = getattr(forward_batch, attr, None)
if cached is not None:
return cached
decoded = self.freqs_cis[(seq_lens - 1) // self.ratio * self.ratio]
setattr(forward_batch, attr, decoded)
return decoded
def forward(
self,
x: torch.Tensor,
forward_batch: ForwardBatch,
attn_backend: AttentionBackend,
) -> torch.Tensor:
if forward_batch.forward_mode.is_idle():
assert x.shape[0] == 0
return x.new_empty(0, self.head_dim)
kv_score = self.compute_kv_score(x, forward_batch)
self.forward_mode = forward_batch.forward_mode
return self.compress_dispatch(
kv_score, forward_batch, attn_backend=attn_backend
)