main
py 98 lines 4.06 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Inkling short convolution: depthwise causal conv1d (+ residual) over a paged
4 sliding-window conv-state cache.
5
6 Each decoder layer owns one ``InklingConvState`` (``sconv_swa_attn.py``) holding the
7 manager-allocated paged cache for the layer's 4 sconv streams (K, V, attn-output,
8 mlp-output), packed head-major into one block. Each ``InklingShortConv`` is a
9 stateless weight + kernel launcher that, per forward (positions-addressed, the
10 same path for prefill / decode / mixed), inserts the current tokens' inputs
11 into their paged slot and convolves each token against the ``W`` taps ending
12 at its absolute position, reading pre-forward window positions out of the
13 paged cache via the block table.
14
15 Per-forward metadata (``block_table`` / ``slot_mapping`` / ``seq_idx`` /
16 ``query_start``) is built once by ``InklingSconvMetadataBuilder`` and published under
17 the owner's prefix in the forward context; the absolute ``positions`` are
18 threaded in from the model. The insert + conv run in a single ``fused_sconv``
19 launch (same path for prefill / decode / mixed / spec). All inputs are
20 fixed-address persistent buffers and the grid is fixed, so the conv replays
21 correctly under eager, PIECEWISE, and FULL cudagraphs.
22 """
23
24 from __future__ import annotations
25
26 import torch
27 from torch import nn
28 from torch.nn.parameter import Parameter
29
30 from vllm.distributed import get_tensor_model_parallel_rank
31 from vllm.forward_context import get_forward_context
32 from vllm.model_executor.utils import set_weight_attrs
33
34 from .ops import fused_sconv
35 from .sconv_swa_attn import InklingConvState, InklingSconvMetadata
36
37
38 class InklingShortConv(nn.Module):
39 def __init__(
40 self, dim: int, kernel_size: int, owner: InklingConvState, stream_idx: int
41 ) -> None:
42 super().__init__()
43 self.dim = dim
44 self.kernel_size = kernel_size
45 self.owner = owner
46 self.stream_idx = stream_idx
47 self.tp_rank = get_tensor_model_parallel_rank()
48
49 # Depthwise conv weight; checkpoint stores (dim, 1, W).
50 self.weight = Parameter(torch.empty(dim, 1, kernel_size), requires_grad=False)
51 set_weight_attrs(self.weight, {"weight_loader": self.weight_loader})
52
53 def weight_loader(self, param: Parameter, loaded_weight: torch.Tensor) -> None:
54 if loaded_weight.shape[0] != param.shape[0]:
55 shard = param.shape[0]
56 loaded_weight = loaded_weight.narrow(0, self.tp_rank * shard, shard)
57 param.data.copy_(loaded_weight)
58
59 def forward(self, x: torch.Tensor, positions: torch.Tensor) -> torch.Tensor:
60 # x: (num_tokens, dim); positions: (num_tokens,) absolute positions.
61 attn_metadata = get_forward_context().attn_metadata
62 if not isinstance(attn_metadata, dict):
63 # Memory-profiling / no metadata: identity (residual).
64 return x
65 m = attn_metadata.get(self.owner.prefix)
66 if m is None:
67 return x
68 assert isinstance(m, InklingSconvMetadata)
69 cache = self.owner.kv_cache
70 if cache.numel() == 0:
71 # Cache not yet bound (profiling before KV alloc): identity.
72 return x
73
74 off_s, ws = self.owner.stream_ranges[self.stream_idx]
75 # The hybrid KV-cache planner can enlarge the logical conv block so
76 # that its physical page size matches the attention caches (for
77 # example, W=4 becomes 32 when --block-size=128). Metadata slot
78 # mappings are built with that enlarged size, so index the bound cache
79 # with its runtime token dimension rather than the kernel window size.
80 block_size = self.owner.cache_block_size
81 x = x.contiguous()
82 weight = self.weight.squeeze(1) # (dim, W)
83
84 return fused_sconv(
85 x,
86 weight,
87 cache,
88 positions,
89 m.block_table,
90 m.seq_idx,
91 m.slot_mapping,
92 m.query_start,
93 off_s,
94 ws,
95 block_size,
96 activation=None,
97 use_residual=True,
98 )