Inkling short-conv state managed as a sliding-window KV cache.
Each decoder layer owns one InklingConvState (an AttentionLayerBase) that emits a single SlidingWindowSpec for the layer's 4 sconv streams (K, V, attn-output, mlp-output), packed head-major into one block:
H = num_kv_heads (per-rank), N = block_size = sconv_kernel_size,
D = head_dim(K) + head_dim(V) + hidden/H(attn) + hidden/H(mlp)
D is TP-invariant; per rank we store H/TP heads of width D. The conv reads/writes this cache out-of-band via a custom backend; the (smaller) conv page is padded up to the uniform attention page by unify_kv_cache_spec_page_size.
Classes:
-
InklingConvState – Per-decoder-layer owner emitting one sliding-window conv-state spec.
-
InklingSconvBackend – Custom dummy backend for the sconv sliding-window cache management.
InklingConvState
Bases: Module, AttentionLayerBase
Per-decoder-layer owner emitting one sliding-window conv-state spec.
Source code in vllm/models/inkling/nvidia/sconv_swa_attn.py
| class InklingConvState(nn.Module, AttentionLayerBase):
"""Per-decoder-layer owner emitting one sliding-window conv-state spec."""
def __init__(
self,
*,
num_kv_heads: int,
head_dim: int,
hidden_size: int,
kernel_size: int,
prefix: str,
) -> None:
super().__init__()
self.prefix = prefix
# Bound to the manager-allocated paged cache by bind_kv_cache; a
# placeholder until then. Read out-of-band by InklingShortConv.
self.kv_cache = torch.tensor([])
tp_size = get_tensor_model_parallel_world_size()
# Guardrails for the conv-state layout below; only these are exercised.
# tp_size <= num_kv_heads keeps >=1 whole KV head per rank (no
# replication/clamping), so the per-head width stays TP-invariant.
assert tp_size <= num_kv_heads, (
f"sconv SWA cache supports tp_size <= num_kv_heads ({num_kv_heads}), "
f"got {tp_size}"
)
# Per-rank head count; D is TP-invariant (K/V heads and the hidden
# chunk both scale 1/TP together). The attn-/mlp-output sconv streams
# are hidden-sharded: each rank owns its H/tp chunk (the sublayer
# outputs are reduce-scattered / all-gathered around the conv).
self.num_kv_heads = num_kv_heads // tp_size
hidden_per_head = hidden_size // num_kv_heads
# Packed per-head width: K + V + attn-output chunk + mlp-output chunk,
# padded to a power of two so every layer's conv page is the same size
# and an exact multiple of the attention page (the page unifier then
# scales attention block sizes instead of padding).
raw_head_size = 2 * head_dim + 2 * hidden_per_head
self.head_size = 1 << (raw_head_size - 1).bit_length()
self.sliding_window = kernel_size
self.block_size = kernel_size
# Per-head D-sub-range (offset, width) for each stream. Streams share
# the cache; each writes/reads its own width across all H heads.
self.stream_ranges: tuple[tuple[int, int], ...] = (
(0, head_dim), # _K
(head_dim, head_dim), # _V
(2 * head_dim, hidden_per_head), # _ATTN
(2 * head_dim + hidden_per_head, hidden_per_head), # _MLP
)
vllm_config = get_current_vllm_config()
self._dtype = vllm_config.model_config.dtype
assert self._dtype == torch.bfloat16, (
f"sconv SWA cache supports bfloat16 only, got {self._dtype}"
)
# Register in the forward context so the runner enumerates this owner as
# an attention-like layer (get_kv_cache_spec / get_attn_backend).
compilation_config = vllm_config.compilation_config
if prefix in compilation_config.static_forward_context:
raise ValueError(f"Duplicate layer name: {prefix}")
compilation_config.static_forward_context[prefix] = self
def forward(self): ...
def get_attn_backend(self) -> type[AttentionBackend]:
return InklingSconvBackend
def get_kv_cache_spec(self, vllm_config: VllmConfig) -> KVCacheSpec:
return SlidingWindowSpec(
block_size=self.block_size,
num_kv_heads=self.num_kv_heads,
head_size=self.head_size,
head_size_v=0, # all 4 streams packed into head_size
dtype=self._dtype,
sliding_window=self.sliding_window,
)
|
InklingSconvBackend
Bases: AttentionBackend
Custom dummy backend for the sconv sliding-window cache management.
Source code in vllm/models/inkling/nvidia/sconv_swa_attn.py
| class InklingSconvBackend(AttentionBackend):
"""Custom dummy backend for the sconv sliding-window cache management."""
@staticmethod
def get_name() -> str:
return "INKLING_SCONV_SWA"
@classmethod
def indexes_kv_by_block_stride(cls) -> bool:
# num_blocks is the outermost dim (HND, see get_kv_cache_shape), so the
# padded conv page is read through a strided view.
return True
@staticmethod
def get_kv_cache_shape(
num_blocks: int,
block_size: int,
num_kv_heads: int,
head_size: int,
cache_dtype_str: str = "auto",
) -> tuple[int, ...]:
# HND, num-blocks-first, head-major: [num_blocks, H, N, D].
return (num_blocks, num_kv_heads, block_size, head_size)
@staticmethod
def get_kv_cache_stride_order(
include_num_layers_dimension: bool = False,
) -> tuple[int, ...]:
# Identity: physical layout == logical [num_blocks, H, N, D].
if include_num_layers_dimension:
return (0, 1, 2, 3, 4)
return (0, 1, 2, 3)
@staticmethod
def get_impl_cls():
raise NotImplementedError(
"InklingSconvBackend has no attention impl; the conv runs out-of-band."
)
@staticmethod
def get_builder_cls() -> type[InklingSconvMetadataBuilder]:
return InklingSconvMetadataBuilder
|