chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,373 @@
|
||||
"""
|
||||
Copyright 2023-2026 SGLang Team
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
MambaCheckpointPool — the radix prefix cache's int8-compressed store for cached
|
||||
linear-attention (KDA / GDN / Mamba2 gated-delta-rule) recurrent states.
|
||||
|
||||
It decouples the *cached* states (radix-owned, idle, compressed) from the *active*
|
||||
``MambaPool`` (running requests, full precision, kernel-facing). The radix stores
|
||||
one cached state per node HERE; on a prefix-cache hit it is dequantized back into
|
||||
a fresh active slot (copy-on-write).
|
||||
|
||||
Per cached slot it holds:
|
||||
* the SSM temporal state in **int8** (per-(head,k-channel) symmetric), via the
|
||||
embedded ``Int8CheckpointStore`` — ~2x more cached states than bf16,
|
||||
quality-safe (quantized once on store, dequantized once on a hit; never
|
||||
re-enters the recurrence as a quant->dequant loop).
|
||||
* the conv1d window state at its native dtype (tiny, W-1 tokens; not worth
|
||||
quantizing).
|
||||
|
||||
Why int8 (not fp8): a cached checkpoint is loaded ONCE on a cache hit, then
|
||||
decoding continues at full precision, so the only error is a single rounding of
|
||||
S. The temporal state is roughly uniformly distributed, so int8-per-(head,
|
||||
k-channel) beats fp8-e4m3 at the same 1 byte (fp8 wastes bits on the exponent).
|
||||
The scale axis (reduces over d_v) matches the per-k-channel decay diag(alpha), so
|
||||
the large state entries keep ~bf16 precision and the error concentrates on small
|
||||
entries that barely affect the readout. Storing cached states int8 gives ~2x the
|
||||
cached-prefix capacity at fixed memory, and composes with host-offload
|
||||
(HiMambaRadixCache) which it also halves.
|
||||
|
||||
This is strategy-agnostic: whether the active slot to be cached was produced by
|
||||
the ``no_buffer`` donate (copy_from) or the ``extra_buffer`` ping-pong track
|
||||
buffer (spec path), both converge on "an active slot becomes the cached
|
||||
``mamba_value``" — which is exactly the (store_from_active) hook here. Slot
|
||||
lifecycle is owned by the caller via the embedded ``MambaSlotAllocator``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import List, Optional
|
||||
|
||||
import torch
|
||||
|
||||
from sglang.srt.mem_cache.allocator.mamba import MambaSlotAllocator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Int8CheckpointStore:
|
||||
"""int8 store for cached multi-layer linear-attn states.
|
||||
|
||||
Tensors (slot index handed out by the caller's allocator):
|
||||
qdata : [L, num_slots, H, d_v, d_k] int8 (the quantized state)
|
||||
scale : [L, num_slots, H, 1, d_k] scale_dtype (per layer,slot,head,k-chan)
|
||||
|
||||
A "state" spans all L mamba layers for one cached point (matching how the
|
||||
radix caches one full state per node). The reduction axis for the scale is
|
||||
d_v (dim=-2), so each (head, k-channel) gets its own scale — aligned with the
|
||||
per-k-channel decay diag(alpha).
|
||||
|
||||
``scale_dtype`` should match the source state's dtype (bf16 / fp16 / fp32) so
|
||||
that quantize and dequantize use the identical scale — it is NOT required to
|
||||
be bf16.
|
||||
"""
|
||||
|
||||
QMAX = 127
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
num_layers: int,
|
||||
num_slots: int,
|
||||
num_heads: int,
|
||||
head_v_dim: int,
|
||||
head_k_dim: int,
|
||||
device: str,
|
||||
scale_dtype: torch.dtype = torch.bfloat16,
|
||||
):
|
||||
self.num_layers = num_layers
|
||||
self.num_slots = num_slots
|
||||
self.H = num_heads
|
||||
self.d_v = head_v_dim
|
||||
self.d_k = head_k_dim
|
||||
self.device = device
|
||||
self.qdata = torch.empty(
|
||||
num_layers,
|
||||
num_slots,
|
||||
num_heads,
|
||||
head_v_dim,
|
||||
head_k_dim,
|
||||
dtype=torch.int8,
|
||||
device=device,
|
||||
)
|
||||
self.scale = torch.empty(
|
||||
num_layers,
|
||||
num_slots,
|
||||
num_heads,
|
||||
1,
|
||||
head_k_dim,
|
||||
dtype=scale_dtype,
|
||||
device=device,
|
||||
)
|
||||
|
||||
# ---- (de)quant math (also usable standalone for probes/tests) ----
|
||||
|
||||
@classmethod
|
||||
def quantize(cls, state: torch.Tensor):
|
||||
"""state [..., H, d_v, d_k] -> (qint8, scale[..., H, 1, d_k]).
|
||||
|
||||
amax / scale / round are computed in float32 so quantizing a low-precision
|
||||
state doesn't lose precision in the intermediate (symmetric with
|
||||
``dequantize``, which is already float32). The scale is rounded to the
|
||||
state dtype (its storage precision) BEFORE the division, so quantize and
|
||||
dequantize use the identical scale."""
|
||||
state_fp32 = state.to(torch.float32)
|
||||
amax = state_fp32.abs().amax(dim=-2, keepdim=True).clamp(min=1e-8)
|
||||
scale = (amax / cls.QMAX).to(state.dtype)
|
||||
q = (
|
||||
torch.round(state_fp32 / scale.to(torch.float32))
|
||||
.clamp(-cls.QMAX, cls.QMAX)
|
||||
.to(torch.int8)
|
||||
)
|
||||
return q, scale
|
||||
|
||||
@staticmethod
|
||||
def dequantize(q: torch.Tensor, scale: torch.Tensor, out_dtype: torch.dtype):
|
||||
return (q.to(torch.float32) * scale.to(torch.float32)).to(out_dtype)
|
||||
|
||||
# ---- store / load (caller supplies slot indices) ----
|
||||
|
||||
def store(self, slots: torch.Tensor, state: torch.Tensor) -> None:
|
||||
"""Quantize and write states. state: [L, N, H, d_v, d_k] for the N slots
|
||||
(or [L, H, d_v, d_k] when slots is a scalar/len-1)."""
|
||||
if state.dim() == 4:
|
||||
state = state.unsqueeze(1)
|
||||
q, scale = self.quantize(state)
|
||||
self.qdata[:, slots] = q
|
||||
self.scale[:, slots] = scale.to(self.scale.dtype)
|
||||
|
||||
def load(self, slots: torch.Tensor, out_dtype: torch.dtype) -> torch.Tensor:
|
||||
"""Dequantize states at slots -> [L, N, H, d_v, d_k] in out_dtype."""
|
||||
return self.dequantize(self.qdata[:, slots], self.scale[:, slots], out_dtype)
|
||||
|
||||
def copy_to_pool(
|
||||
self,
|
||||
dst_temporal: torch.Tensor,
|
||||
src_slots: torch.Tensor,
|
||||
dst_slots: torch.Tensor,
|
||||
) -> None:
|
||||
"""Dequantize checkpoints at ``src_slots`` directly into the active pool
|
||||
tensor ``dst_temporal`` [L, pool_slots, H, d_v, d_k] at ``dst_slots`` (the
|
||||
copy-on-write on a cache hit). Output dtype follows ``dst_temporal``."""
|
||||
dst_temporal[:, dst_slots] = self.load(src_slots, dst_temporal.dtype)
|
||||
|
||||
def store_from_pool(
|
||||
self,
|
||||
src_temporal: torch.Tensor,
|
||||
src_slots: torch.Tensor,
|
||||
dst_slots: torch.Tensor,
|
||||
) -> None:
|
||||
"""Quantize states from an active pool tensor into checkpoint slots (cache
|
||||
store / donate)."""
|
||||
self.store(dst_slots, src_temporal[:, src_slots])
|
||||
|
||||
def mem_usage_bytes(self) -> int:
|
||||
return (
|
||||
self.qdata.numel() * self.qdata.element_size()
|
||||
+ self.scale.numel() * self.scale.element_size()
|
||||
)
|
||||
|
||||
def bytes_per_slot(self) -> int:
|
||||
return self.mem_usage_bytes() // max(1, self.num_slots)
|
||||
|
||||
|
||||
class MambaCheckpointPool:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
num_layers: int,
|
||||
num_slots: int,
|
||||
num_heads: int,
|
||||
head_v_dim: int,
|
||||
head_k_dim: int,
|
||||
conv_shapes: List[tuple],
|
||||
conv_dtype: torch.dtype,
|
||||
device: str,
|
||||
temporal_dtype: Optional[torch.dtype] = None,
|
||||
):
|
||||
self.num_slots = num_slots
|
||||
self.device = device
|
||||
self.temporal = Int8CheckpointStore(
|
||||
num_layers=num_layers,
|
||||
num_slots=num_slots + 1, # slot 0 reserved (matches MambaSlotAllocator)
|
||||
num_heads=num_heads,
|
||||
head_v_dim=head_v_dim,
|
||||
head_k_dim=head_k_dim,
|
||||
device=device,
|
||||
# store the scale in the temporal state's own dtype so quantize and
|
||||
# dequantize use the identical scale (not hard-coded to bf16)
|
||||
scale_dtype=(
|
||||
temporal_dtype if temporal_dtype is not None else torch.bfloat16
|
||||
),
|
||||
)
|
||||
# conv windows stay at their native dtype (small); one buffer per conv
|
||||
# tensor in the State
|
||||
self.conv = [
|
||||
torch.empty(
|
||||
(num_layers, num_slots + 1) + tuple(shape),
|
||||
dtype=conv_dtype,
|
||||
device=device,
|
||||
)
|
||||
for shape in conv_shapes
|
||||
]
|
||||
self.allocator = MambaSlotAllocator(size=num_slots, device=device)
|
||||
|
||||
# ---- lifecycle (delegates to the embedded allocator) ----
|
||||
|
||||
def alloc(self, n: int = 1):
|
||||
return self.allocator.alloc(n)
|
||||
|
||||
def free(self, slots: torch.Tensor):
|
||||
self.allocator.free(slots)
|
||||
|
||||
def available_size(self) -> int:
|
||||
return self.allocator.available_size()
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Release every checkpoint slot (radix flush/reset). The int8 qdata is
|
||||
left as-is; slots are reused/overwritten on the next store."""
|
||||
self.allocator.clear()
|
||||
|
||||
# ---- state transfer between the active MambaPool and this store ----
|
||||
|
||||
def store_from_active(self, active_mamba_pool, active_slots, ckpt_slots) -> None:
|
||||
"""Quantize temporal + copy conv from the active pool into checkpoint slots
|
||||
(the radix donate / cache-store)."""
|
||||
cache = active_mamba_pool.mamba_cache
|
||||
self.temporal.store_from_pool(cache.temporal, active_slots, ckpt_slots)
|
||||
for i, c in enumerate(self.conv):
|
||||
c[:, ckpt_slots] = cache.conv[i][:, active_slots]
|
||||
|
||||
def load_to_active(self, active_mamba_pool, ckpt_slots, active_slots) -> None:
|
||||
"""Dequantize temporal + copy conv from checkpoint slots into the active pool
|
||||
(the cache-hit copy-on-write)."""
|
||||
cache = active_mamba_pool.mamba_cache
|
||||
self.temporal.copy_to_pool(cache.temporal, ckpt_slots, active_slots)
|
||||
for i, c in enumerate(self.conv):
|
||||
cache.conv[i][:, active_slots] = c[:, ckpt_slots].to(cache.conv[i].dtype)
|
||||
|
||||
@staticmethod
|
||||
def estimate_mem_usage_bytes(
|
||||
*,
|
||||
num_layers: int,
|
||||
num_slots: int,
|
||||
num_heads: int,
|
||||
head_v_dim: int,
|
||||
head_k_dim: int,
|
||||
conv_shapes: List[tuple],
|
||||
conv_dtype: torch.dtype,
|
||||
temporal_dtype: torch.dtype,
|
||||
) -> dict:
|
||||
"""Estimate the pool's HBM footprint (bytes) WITHOUT allocating, so a
|
||||
caller can check it against free memory before construction. Mirrors the
|
||||
real layout: int8 qdata + per-(head,k) scale + bf16 conv windows, including
|
||||
the reserved slot 0."""
|
||||
slots = num_slots + 1 # slot 0 reserved (matches MambaSlotAllocator)
|
||||
scale_isz = torch.empty((), dtype=temporal_dtype).element_size()
|
||||
conv_isz = torch.empty((), dtype=conv_dtype).element_size()
|
||||
qdata = num_layers * slots * num_heads * head_v_dim * head_k_dim # int8 = 1B
|
||||
scale = num_layers * slots * num_heads * head_k_dim * scale_isz
|
||||
conv = 0
|
||||
for shape in conv_shapes:
|
||||
n = 1
|
||||
for s in shape:
|
||||
n *= int(s)
|
||||
conv += num_layers * slots * n * conv_isz
|
||||
return {
|
||||
"qdata": qdata,
|
||||
"scale": scale,
|
||||
"conv": conv,
|
||||
"total": qdata + scale + conv,
|
||||
}
|
||||
|
||||
def mem_usage_bytes(self) -> int:
|
||||
conv_bytes = sum(c.numel() * c.element_size() for c in self.conv)
|
||||
return self.temporal.mem_usage_bytes() + conv_bytes
|
||||
|
||||
|
||||
def maybe_init_int8_mamba_checkpoint_pool(
|
||||
*,
|
||||
mamba_size: int,
|
||||
cache_params,
|
||||
mamba_layer_ids: List[int],
|
||||
device: str,
|
||||
) -> Optional[MambaCheckpointPool]:
|
||||
"""Build the optional int8 ``MambaCheckpointPool`` when
|
||||
``--enable-int8-mamba-checkpoint`` is set (and a global server-args context
|
||||
exists), else return ``None``. The radix caches states here (int8) instead of
|
||||
in the active bf16 pool -> ~2x cached-prefix capacity at fixed memory.
|
||||
|
||||
Estimates the pool's HBM footprint and checks it against free memory BEFORE
|
||||
allocating, so an oversized ``--int8-mamba-ckpt-size`` fails with an actionable
|
||||
message instead of a cryptic mid-allocation CUDA OOM.
|
||||
"""
|
||||
from sglang.srt.runtime_context import get_server_args
|
||||
|
||||
try:
|
||||
_sa = get_server_args()
|
||||
except ValueError:
|
||||
# Some unit-test / mock runners construct HybridReqToTokenPool directly
|
||||
# without a global server-args context. The int8 checkpoint pool is opt-in
|
||||
# via a CLI flag, so an unset context unambiguously means it is off.
|
||||
_sa = None
|
||||
if not getattr(_sa, "enable_int8_mamba_checkpoint", False):
|
||||
return None
|
||||
|
||||
GB = 1 << 30
|
||||
H, d_v, d_k = cache_params.shape.temporal
|
||||
ckpt_size = _sa.int8_mamba_ckpt_size or (2 * mamba_size)
|
||||
kwargs = dict(
|
||||
num_layers=len(mamba_layer_ids),
|
||||
num_slots=ckpt_size,
|
||||
num_heads=H,
|
||||
head_v_dim=d_v,
|
||||
head_k_dim=d_k,
|
||||
conv_shapes=list(cache_params.shape.conv),
|
||||
conv_dtype=cache_params.dtype.conv,
|
||||
temporal_dtype=cache_params.dtype.temporal,
|
||||
)
|
||||
|
||||
est = MambaCheckpointPool.estimate_mem_usage_bytes(**kwargs)
|
||||
free_bytes = None
|
||||
if isinstance(device, str) and device.startswith("cuda"):
|
||||
try:
|
||||
free_bytes, _ = torch.cuda.mem_get_info(device)
|
||||
except Exception:
|
||||
free_bytes = None
|
||||
logger.info(
|
||||
f"int8 mamba checkpoint pool: {ckpt_size} slots, "
|
||||
f"{est['total'] / GB:.2f}GB (qdata {est['qdata'] / GB:.2f} + scale "
|
||||
f"{est['scale'] / GB:.2f} + conv {est['conv'] / GB:.2f}); active mamba "
|
||||
f"pool {mamba_size} slots"
|
||||
+ (f"; free HBM {free_bytes / GB:.2f}GB" if free_bytes is not None else "")
|
||||
)
|
||||
if free_bytes is not None and est["total"] >= free_bytes:
|
||||
raise RuntimeError(
|
||||
f"int8 mamba checkpoint pool needs ~{est['total'] / GB:.2f}GB but only "
|
||||
f"{free_bytes / GB:.2f}GB HBM is free. Lower --int8-mamba-ckpt-size "
|
||||
f"(currently {ckpt_size}) or --mem-fraction-static."
|
||||
)
|
||||
|
||||
pool = MambaCheckpointPool(device=device, **kwargs)
|
||||
# NOTE: this pool's HBM is NOT subtracted from the KV-cache budget
|
||||
# (max_total_num_tokens); it is allocated from --mem-fraction-static headroom.
|
||||
# The estimate check above guards against an oversized pool; accounting it in
|
||||
# the KV budget is a follow-up.
|
||||
logger.warning(
|
||||
f"int8 mamba checkpoint pool ({est['total'] / GB:.2f}GB) is allocated from "
|
||||
f"--mem-fraction-static headroom and is not reflected in "
|
||||
f"max_total_num_tokens; ensure headroom covers it."
|
||||
)
|
||||
return pool
|
||||
Reference in New Issue
Block a user