Download code/offload/dspark/worker/model.py from ProprietaryLegal/dsv41-sampled-drafting: direct link, hf CLI and curl.
- Browser
- Download file 36.9 kB
-
https://huggingface.co/ProprietaryLegal/dsv41-sampled-drafting/resolve/main/code/offload/dspark/worker/model.py
- Command line
-
hf download hf://ProprietaryLegal/dsv41-sampled-drafting/code/offload/dspark/worker/model.py
-
curl -L -o model.py https://huggingface.co/ProprietaryLegal/dsv41-sampled-drafting/resolve/main/code/offload/dspark/worker/model.py
36.9 kB
| """The DSpark drafter of the served checkpoint on one RTX 3090 (SM86): weights, per-request window state, draft step. | |
| Semantics: NOTES.md (the engine's DSpark round, traced from the running image). Tensors: the exact checkpoint bytes | |
| (checkpoint.Checkpoint refuses anything else). Representation changes are draft-only and listed in NOTES.md. | |
| Public surface (all device tensors, no host syncs inside, so the draft step and the insert capture into CUDA graphs): | |
| * ``main_x(taps)`` [n, 15360] bf16 target taps -> [n, 5120] bf16 conditioning features | |
| * ``insert(slot_rows, pos_rows, mx)`` write the context KV of accepted positions into request windows | |
| * ``draft(slots, anchors, last_pos, k)`` -> (draft ids [B, k] int64, confidences [B, k] fp32) | |
| * ``reset_slots(slots)`` empty request windows | |
| Graphs: ``capture(batch_sizes, ks, n_outs)`` records one graph per (B, k, n_out) (and a margin twin); | |
| ``draft_graphed`` replays it. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| import time | |
| from dataclasses import dataclass, field | |
| import torch | |
| from . import env # noqa: F401 | |
| from . import fused as FU | |
| from .checkpoint import Checkpoint, DrafterShape | |
| from .exl3 import kernels as K | |
| from .exl3.bank import Exl3Bank | |
| from .exl3.plan import PROJ_DOWN, PROJ_GATE, PROJ_UP, Exl3MoEPlan | |
| from .ops import (MarkovBias, apply_rope, block_attention, hc_collapse, hc_mixes, hc_post, kv_record_qdq, rms_norm, | |
| route, rope_cos_sin) | |
| MUL1 = 0x83DCD12D - (1 << 32) # the mul1 codebook marker as int32 | |
| class Window: | |
| """What a draft block attends to: the ring rows of ``slots`` whose position lies in [lo, last] (int64 [B] each). | |
| ``kv`` [B, n_stages, W, D] / ``valid`` [B, W] are the gathered rows, filled only for the unfused (ops) path; the | |
| fused path reads the ring in place (fused.block_attention_ring).""" | |
| slots: torch.Tensor | |
| lo: torch.Tensor | |
| last: torch.Tensor | |
| kv: torch.Tensor | None = None | |
| valid: torch.Tensor | None = None | |
| def facc_ext(): | |
| """exllamav3_ext_facc: the same exllamav3 1.6.0 source built with fp32 MMA accumulation on sm_86 (stock 1.6.0 | |
| accumulates exl3_gemm in fp16 on sm_86 only, EXL3_GEMM_H_ACC; ENV.md). Raises if it is not installed.""" | |
| import exllamav3_ext_facc # type: ignore[import-not-found] | |
| return exllamav3_ext_facc | |
| class Exl3Linear: | |
| """One EXL3 matrix on exllamav3 1.6.0: fp16 input, fp32 output (the plugin's prompt-size path; NOTES.md). | |
| ``prescale``: divide the input by the power of two that brings max |x| under 2^14 and multiply the output back | |
| (exact; the plugin does this for main_proj, whose input is the target's raw residual stream). | |
| ``ext`` / ``force_shape``: call ``ext.exl3_gemm`` directly (``ext`` defaults to the stock exllamav3_ext) with a | |
| fixed kernel shape instead of LinearEXL3's timing-based autotune: the kernel, and so each row's result, is then | |
| the same for every row count the shape covers (1..16 for the 16-row shapes) and in every process.""" | |
| def __init__(self, trellis: torch.Tensor, suh: torch.Tensor, svh: torch.Tensor, k: int, dev: torch.device, | |
| prescale: bool = False, ext=None, force_shape: int = 0): | |
| from exllamav3.modules.quant.exl3 import LinearEXL3 | |
| self.in_features, self.out_features, self.k = trellis.shape[0] * 16, trellis.shape[1] * 16, k | |
| self.trellis = trellis.to(dev).contiguous() | |
| self.suh = suh.to(dev).contiguous() | |
| self.svh = svh.to(dev).contiguous() | |
| self.mul1 = torch.tensor(MUL1, dtype=torch.int32, device=dev) | |
| self.lin = LinearEXL3(None, self.in_features, self.out_features, suh=self.suh, svh=self.svh, | |
| trellis=self.trellis, mul1=self.mul1) | |
| self.prescale = prescale | |
| self.direct = ext is not None or force_shape > 0 | |
| if self.direct: | |
| if ext is None: | |
| import exllamav3_ext as ext | |
| self.ext = ext | |
| self.force_shape = force_shape | |
| def _gemm(self, x16: torch.Tensor, out: torch.Tensor | None) -> torch.Tensor: | |
| if not self.direct: | |
| if out is not None: | |
| raise ValueError("out= needs the direct exl3_gemm path") | |
| return self.lin.forward(x16, {}, out_dtype=torch.float32) | |
| if out is None: | |
| out = torch.empty(x16.shape[0], self.out_features, dtype=torch.float32, device=x16.device) | |
| self.ext.exl3_gemm(x16, self.trellis, out, self.suh, torch.empty_like(x16), self.svh, self.force_shape, False, | |
| True, 0) | |
| return out | |
| def __call__(self, x: torch.Tensor, out: torch.Tensor | None = None) -> torch.Tensor: | |
| """x [m, in] (bf16 / fp32 are converted to fp16; fp16 is used as is) -> fp32 [m, out].""" | |
| if self.prescale: | |
| sc = torch.exp2(torch.ceil(torch.log2(x.abs().amax().float().clamp_min(1.0) / 16384.0)).clamp_min(0.0)) | |
| y = self._gemm((x.float() / sc).to(torch.float16).contiguous(), out) | |
| return y.mul_(sc) | |
| if x.dtype != torch.float16: | |
| x = x.to(torch.float16) | |
| return self._gemm(x.contiguous(), out) | |
| def nbytes(self) -> int: | |
| return sum(t.numel() * t.element_size() for t in (self.trellis, self.suh, self.svh)) | |
| class Stage: | |
| wq_a: Exl3Linear | |
| wkv: Exl3Linear | |
| wq_b: Exl3Linear | |
| wo_b: Exl3Linear | |
| wo_a8: torch.Tensor # uint8 [groups * 1024, 4096]: wo_a's e4m3fn bytes (fused path: fused.wo_a_fp8) | |
| wo_a_sc: torch.Tensor # fp32 [256, 128]: its UE8M0 32 x 32 block scales | |
| wo_a_t: torch.Tensor | None # bf16 [groups, 4096, 1024]: wo_a dequantized exactly, transposed per group (unfused | |
| # path and tools; None when fused, see DSparkDrafter.wo_a_bf16) | |
| attn_sink: torch.Tensor # fp32 [heads] | |
| q_norm: torch.Tensor | |
| kv_norm: torch.Tensor | |
| attn_norm: torch.Tensor | |
| ffn_norm: torch.Tensor | |
| hc_attn: tuple[torch.Tensor, torch.Tensor, torch.Tensor] # fn fp32 [24, 4H], scale [3], base [24] | |
| hc_ffn: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | |
| gate_w: torch.Tensor # fp32 [E, H] | |
| gate_bias: torch.Tensor # fp32 [E] | |
| sh_w1: Exl3Linear | |
| sh_w3: Exl3Linear | |
| sh_w2: Exl3Linear | |
| bank: Exl3Bank | |
| groups: list | |
| perm: torch.Tensor # int64 [E] logical -> physical slot | |
| def dequant_wo_a(weight: torch.Tensor, scale: torch.Tensor, groups: int) -> torch.Tensor: | |
| """FP8 e4m3 [G*R, C] x UE8M0 block scales [G*R/bs, C/bs] -> bf16 [G, C, R] (exact: e4m3 x 2^k is a bf16 value).""" | |
| rows, cols = weight.shape | |
| br, bc = rows // scale.shape[0], cols // scale.shape[1] | |
| s = scale.float().repeat_interleave(br, 0).repeat_interleave(bc, 1) | |
| w = (weight.float() * s).to(torch.bfloat16) | |
| if not torch.equal(w.float(), weight.float() * s): | |
| raise RuntimeError("wo_a dequantization is not exact in bf16") | |
| return w.view(groups, rows // groups, cols).transpose(1, 2).contiguous() | |
| class DSparkDrafter: | |
| """All weights resident on one GPU; per-request windows in ``slots`` fixed slots (slot ``slots`` = scratch).""" | |
| HEAD_SHAPE = 3 # exl3_gemm shape (16, 32, 256): what the autotuner picks for the head at 1-16 rows (689 us) | |
| HEAD_CHUNK = 16 # head rows per GEMM call: every call stays on the 16-row shape (n_out bit-identity) | |
| def __init__(self, ck: Checkpoint, dev: torch.device, slots: int = 16, max_model_len: int = 327680, | |
| max_rows: int = 32, kv_format: str = "v4", fused: bool = True, main_proj_fp32acc: bool = True, | |
| markov_cache_rows: int = 8192): | |
| if not ck.verified: | |
| raise RuntimeError("checkpoint not identity-verified") | |
| t0 = time.time() | |
| self.dev = dev | |
| self.s: DrafterShape = ck.shape | |
| s = self.s | |
| self.kv_format = kv_format # "v4": the SM12x fp8_ds_mla record values; "bf16": no quantization | |
| self.fused = fused # Triton kernels of fused.py (False: the plain torch ops of ops.py) | |
| torch.backends.cuda.matmul.allow_tf32 = False | |
| torch.backends.cuda.matmul.allow_bf16_reduced_precision_reduction = False | |
| self.max_model_len = max_model_len | |
| self.max_pos = max_model_len + 2048 # RoPE rows, like the engine's cap (patch 0033a) | |
| self.max_rows = max_rows | |
| self.eps = s.rms_eps | |
| self.scale = s.head_dim ** -0.5 | |
| k_dense = ck.drafter_k | |
| plan = Exl3MoEPlan.from_dict(ck.plan_json) | |
| def lin(name: str, want_k: int | None, prescale: bool = False, ext=None, force_shape: int = 0) -> Exl3Linear: | |
| tr, suh, svh, k = ck.exl3(name) | |
| if want_k is not None and k != want_k: | |
| raise RuntimeError(f"{name}: trellis K{k}, config says K{want_k}") | |
| return Exl3Linear(tr, suh, svh, k, dev, prescale, ext, force_shape) | |
| def g(name: str, dtype: torch.dtype | None = None) -> torch.Tensor: | |
| t = ck.get(name) | |
| return t.to(dev, dtype=dtype or t.dtype).contiguous() | |
| self.embed = g("embed.weight") # bf16 [V, H] | |
| self.head = lin("head", ck.head_k, force_shape=self.HEAD_SHAPE) | |
| # main_proj with fp32 accumulation: its 15360-wide input (three nearly equal target residual streams) cancels | |
| # heavily, and exllamav3's sm_86 fp16 accumulators put main_x 0.37 % from fp32 (the engine: 0.11 %; D3b) | |
| self.main_proj_fp32acc = main_proj_fp32acc | |
| self.main_proj = lin("mtp.0.main_proj", k_dense["main_proj"], prescale=True, | |
| ext=facc_ext() if main_proj_fp32acc else None) | |
| self.main_norm = g("mtp.0.main_norm.weight") | |
| last = f"mtp.{s.n_stages - 1}." | |
| self.norm = g(last + "norm.weight") | |
| self.conf_w = g(last + "confidence_head.proj.weight", torch.float32).view(-1) | |
| tokens = None | |
| tok_path = os.path.join(ck.root, "markov_top_tokens.json") | |
| if os.path.exists(tok_path): | |
| with open(tok_path) as f: | |
| tokens = json.load(f) | |
| # cached Markov bias rows: the serve caches the first 4,096 ids of markov_top_tokens.json; the worker caches all | |
| # 8,192 (same kernel, same bits; 80 % instead of 74 % of the live captures' Markov inputs hit the cache, each | |
| # miss costs a 66 MB GEMV, ~85 us on the 3090). +1 GB. | |
| self.markov = MarkovBias(g(last + "markov_head.embed.weight"), g(last + "markov_head.head.weight"), tokens, | |
| rows=markov_cache_rows) | |
| self.stages: list[Stage] = [] | |
| for i in range(s.n_stages): | |
| p = f"mtp.{i}." | |
| L = s.n_target_layers + i | |
| lp = plan.drafter_layers[L] | |
| if lp.num_experts != s.n_experts: | |
| raise RuntimeError(f"plan layer {L}: {lp.num_experts} experts, config {s.n_experts}") | |
| bank = Exl3Bank(lp, s.hidden, s.inter, 1, 0) | |
| bank.allocate(dev) | |
| for e in range(s.n_experts): | |
| for proj, wn in ((PROJ_GATE, "w1"), (PROJ_UP, "w3"), (PROJ_DOWN, "w2")): | |
| tr, suh, svh, _ = ck.exl3(f"{p}ffn.experts.{e}.{wn}") | |
| bank.load_piece("trellis", proj, e, tr) | |
| bank.load_piece("suh", proj, e, suh) | |
| bank.load_piece("svh", proj, e, svh) | |
| groups, _ = bank.tables() | |
| st = Stage( | |
| wq_a=lin(p + "attn.wq_a", k_dense["attn.wq_a"]), wkv=lin(p + "attn.wkv", k_dense["attn.wq_a"]), | |
| wq_b=lin(p + "attn.wq_b", k_dense["attn.wq_b"]), wo_b=lin(p + "attn.wo_b", k_dense["attn.wo_b"]), | |
| wo_a8=g(p + "attn.wo_a.weight").view(torch.uint8), wo_a_sc=g(p + "attn.wo_a.scale", torch.float32), | |
| wo_a_t=None if fused else dequant_wo_a(ck.get(p + "attn.wo_a.weight").to(dev), | |
| ck.get(p + "attn.wo_a.scale").to(dev), s.o_groups), | |
| attn_sink=g(p + "attn.attn_sink", torch.float32), q_norm=g(p + "attn.q_norm.weight"), | |
| kv_norm=g(p + "attn.kv_norm.weight"), attn_norm=g(p + "attn_norm.weight"), | |
| ffn_norm=g(p + "ffn_norm.weight"), | |
| hc_attn=(g(p + "hc_attn_fn"), g(p + "hc_attn_scale"), g(p + "hc_attn_base")), | |
| hc_ffn=(g(p + "hc_ffn_fn"), g(p + "hc_ffn_scale"), g(p + "hc_ffn_base")), | |
| gate_w=g(p + "ffn.gate.weight", torch.float32), gate_bias=g(p + "ffn.gate.bias", torch.float32), | |
| sh_w1=lin(p + "ffn.shared_experts.w1", k_dense["ffn.shared_experts.w1"]), | |
| sh_w3=lin(p + "ffn.shared_experts.w3", k_dense["ffn.shared_experts.w1"]), | |
| sh_w2=lin(p + "ffn.shared_experts.w2", k_dense["ffn.shared_experts.w2"]), | |
| bank=bank, groups=groups, perm=torch.tensor(lp.perm, dtype=torch.int64, device=dev), | |
| ) | |
| self.stages.append(st) | |
| from .exl3.hostcopy import release_stage | |
| release_stage() | |
| self.coop = K.get_coop_scratch(dev, max_rows, s.top_k, s.hidden, s.inter) | |
| K.prime(dev, self.stages[0].groups, self.coop, None, s.hidden, s.top_k, s.swiglu_limit, False, True) | |
| self.cos_sin = rope_cos_sin(self.max_pos, s.rope_dim, s.rope_theta).to(dev) | |
| # query positions are clamped to max_model_len - 1 (the engine's clamp); a device scalar so CUDA graphs read | |
| # the current value (set_query_clamp) | |
| self.q_clamp = torch.tensor(max_model_len - 1, dtype=torch.int64, device=dev) | |
| # per-request windows: slot `slots` is a scratch slot that padded rows write to | |
| self.n_slots = slots | |
| self.ring_kv = torch.zeros(slots + 1, s.n_stages, s.window, s.head_dim, dtype=torch.bfloat16, device=dev) | |
| self.ring_pos = torch.full((slots + 1, s.window), -1, dtype=torch.int64, device=dev) | |
| self.arange_block = torch.arange(s.block + 1, dtype=torch.int64, device=dev) | |
| self.graphs: dict[tuple[int, int, int], dict] = {} | |
| self.margin_graphs: dict[tuple[int, int, int], dict] = {} | |
| self.reply_graphs: dict[tuple[int, int, int, bool], dict] = {} | |
| self.insert_graphs: dict[int, dict] = {} | |
| self.pool = None | |
| torch.cuda.synchronize(dev) | |
| self.load_seconds = time.time() - t0 | |
| def wo_a_bf16(self, i: int) -> torch.Tensor: | |
| """Stage i's wo_a as bf16 [groups, 4096, 1024] (exact dequantization; for tools on the fused model).""" | |
| st = self.stages[i] | |
| if st.wo_a_t is not None: | |
| return st.wo_a_t | |
| return dequant_wo_a(st.wo_a8.view(torch.float8_e4m3fn), st.wo_a_sc, self.s.o_groups) | |
| # ------------------------------------------------------------------------------------------ op selection | |
| def _norm(self, x: torch.Tensor, w: torch.Tensor) -> torch.Tensor: | |
| return FU.rms_norm(x, w, self.eps) if self.fused else rms_norm(x.to(torch.bfloat16), w, self.eps) | |
| def _rope(self, x: torch.Tensor, pos: torch.Tensor, inverse: bool = False) -> torch.Tensor: | |
| return FU.rope(x, pos, self.cos_sin, inverse) if self.fused else apply_rope(x, pos, self.cos_sin, inverse) | |
| def _kv(self, kv: torch.Tensor, pos: torch.Tensor) -> torch.Tensor: | |
| """rope + (V4 record) of normed KV rows.""" | |
| if self.kv_format != "v4": | |
| return self._rope(kv, pos) | |
| if self.fused: | |
| return FU.kv_rope_qdq(kv, pos, self.cos_sin) | |
| return kv_record_qdq(apply_rope(kv, pos, self.cos_sin)) | |
| def _mixes(self, stream, fn, scale, base): | |
| f = FU.hc_mixes if self.fused else hc_mixes | |
| return f(stream, fn, scale, base, self.eps, self.s.hc_eps, self.s.hc_iters) | |
| def _collapse_norm(self, stream, pre, w): | |
| if self.fused: | |
| return FU.collapse_norm(stream, pre, w, self.eps) | |
| return rms_norm(hc_collapse(stream, pre), w, self.eps) | |
| def _post(self, y, stream, post, comb): | |
| return (FU.hc_post if self.fused else hc_post)(y, stream, post, comb) | |
| def _collapse(self, stream, pre): | |
| return FU.collapse(stream, pre) if self.fused else hc_collapse(stream, pre) | |
| # ------------------------------------------------------------------------------------------ context side | |
| def main_x(self, taps: torch.Tensor) -> torch.Tensor: | |
| """[n, 3H] bf16 taps of target layers 37/38/39 -> main_x [n, H] bf16.""" | |
| if self.fused and isinstance(self.main_proj, Exl3Linear): | |
| x16, sc = FU.prescale_fp16(taps) | |
| return FU.scaled_rms_norm(self.main_proj._gemm(x16, None), sc, self.main_norm, self.eps) | |
| return self._norm(self.main_proj(taps), self.main_norm) | |
| def context_kv(self, mx: torch.Tensor, pos: torch.Tensor) -> torch.Tensor: | |
| """Stage context KV [n_stages, n, D] (rope at pos, record-quantized) of main_x rows.""" | |
| out = [] | |
| for st in self.stages: | |
| out.append(self._kv(self._norm(st.wkv(mx), st.kv_norm), pos)) | |
| return torch.stack(out) | |
| def insert(self, slot_rows: torch.Tensor, pos_rows: torch.Tensor, mx: torch.Tensor) -> None: | |
| """Write the context KV of rows (slot, position, main_x) into the windows. Callers pass at most the last | |
| `window` positions of a request per call (older ones would be overwritten anyway; duplicates are refused | |
| upstream). Padded rows use slot == n_slots (scratch).""" | |
| if self.fused and self.kv_format == "v4": | |
| mx16 = mx.to(torch.float16) | |
| for i, st in enumerate(self.stages): | |
| FU.kv_store(st.wkv(mx16), st.kv_norm, self.eps, pos_rows, self.cos_sin, self.ring_kv[:, i], slot_rows, | |
| self.ring_pos if i == 0 else None) | |
| return | |
| kv = self.context_kv(mx, pos_rows) # [S, n, D] | |
| idx = pos_rows % self.s.window | |
| self.ring_kv[slot_rows, :, idx] = kv.transpose(0, 1) | |
| self.ring_pos[slot_rows, idx] = pos_rows | |
| def reset_slots(self, slots: torch.Tensor) -> None: | |
| self.ring_pos[slots] = -1 | |
| self.ring_kv[slots] = 0 | |
| # ------------------------------------------------------------------------------------------ draft step | |
| def _attention(self, st: Stage, i: int, x: torch.Tensor, pos: torch.Tensor, win: Window, B: int, k: int | |
| ) -> torch.Tensor: | |
| s = self.s | |
| T = B * k | |
| x16 = x.to(torch.float16) | |
| qr = self._norm(st.wq_a(x16), st.q_norm) | |
| kvb = self._kv(self._norm(st.wkv(x16), st.kv_norm), pos) | |
| q = self._rope(st.wq_b(qr).to(torch.bfloat16).view(T, s.n_heads, s.head_dim), pos) | |
| qb, kb = q.view(B, k, s.n_heads, s.head_dim), kvb.view(B, k, s.head_dim) | |
| if win.kv is None: | |
| o = FU.block_attention_ring(qb, self.ring_kv[:, i], self.ring_pos, win.slots, win.lo, win.last, kb, | |
| st.attn_sink, self.scale) | |
| else: | |
| o = block_attention(qb, win.kv[:, i], win.valid, kb, st.attn_sink, self.scale) | |
| o = self._rope(o.view(T, s.n_heads, s.head_dim), pos, inverse=True) | |
| if st.wo_a_t is None: | |
| z = FU.wo_a_fp8(o.view(T, -1), st.wo_a8, st.wo_a_sc, s.o_groups) # [T, G*1024] bf16 | |
| else: | |
| og = o.view(T, s.o_groups, -1).transpose(0, 1) # [G, T, 4096] | |
| z = torch.bmm(og, st.wo_a_t).transpose(0, 1).reshape(T, -1) # [T, G*1024] bf16 | |
| return st.wo_b(z).to(torch.bfloat16) | |
| def _moe(self, st: Stage, x: torch.Tensor, trace: dict | None = None, tag: str = "") -> torch.Tensor: | |
| s = self.s | |
| ids, w = route(x, st.gate_w, st.gate_bias, s.top_k, s.route_scale) | |
| if trace is not None: | |
| trace[tag + ".experts"] = ids | |
| trace[tag + ".route_w"] = w | |
| x16 = x.to(torch.float16).contiguous() | |
| sel = st.perm[ids].contiguous() | |
| routed = K.run_decode_coop(x16, sel, w.to(torch.float16).contiguous(), st.groups, self.coop, | |
| s.swiglu_limit, False, True) | |
| g = st.sh_w1(x16) | |
| u = st.sh_w3(x16) | |
| lim = s.swiglu_limit | |
| a = torch.nn.functional.silu(g.clamp(max=lim)) * u.clamp(min=-lim, max=lim) | |
| sh = st.sh_w2(a.to(torch.float16)) | |
| return (routed + sh).to(torch.bfloat16) | |
| def forward_block(self, ids: torch.Tensor, pos: torch.Tensor, win: Window, trace: dict | None = None | |
| ) -> torch.Tensor: | |
| """The three stages over a query block. ids/pos [B, k]; win: the context window (block_inputs). | |
| Returns head_hidden [B*k, H] bf16 (pre-norm). ``trace`` (eager only) collects per-stage tensors.""" | |
| s = self.s | |
| B, k = ids.shape | |
| T = B * k | |
| pos_f = pos.reshape(T) | |
| x = self.embed[ids.reshape(T)] | |
| stream = x.unsqueeze(1).expand(T, s.hc_mult, s.hidden).contiguous() | |
| pre_in = None | |
| for i, st in enumerate(self.stages): | |
| pre, post, comb = self._mixes(stream, *st.hc_attn) | |
| xin = self._collapse_norm(stream, pre_in, st.attn_norm) | |
| a = self._attention(st, i, xin, pos_f, win, B, k) | |
| stream = self._post(a, stream, post, comb) | |
| fpre, fpost, fcomb = self._mixes(stream, *st.hc_ffn) | |
| xin = self._collapse_norm(stream, pre, st.ffn_norm) | |
| y = self._moe(st, xin, trace, f"stage{i}") | |
| stream = self._post(y, stream, fpost, fcomb) | |
| pre_in = fpre | |
| if trace is not None: | |
| trace[f"stage{i}.attn_out"] = a | |
| trace[f"stage{i}.ffn_in"] = xin | |
| trace[f"stage{i}.ffn_out"] = y | |
| trace[f"stage{i}.stream"] = stream | |
| return self._collapse(stream, pre_in) | |
| def heads(self, hh: torch.Tensor, anchors: torch.Tensor, B: int, k: int, trace: dict | None = None, | |
| n_out: int | None = None) -> tuple[torch.Tensor, torch.Tensor]: | |
| """Greedy sequential Markov drafting + confidence over head_hidden [B*k, H], for the first ``n_out`` (default | |
| k) positions of every request; later positions return draft 0 and confidence 0. Positions < n_out are | |
| bit-identical for every n_out (CONTRACT 1.3): head rows go through the GEMM in chunks of <= HEAD_CHUNK rows on | |
| one fixed kernel shape, Markov and argmax are per row, and the confidence product keeps its [B, k] shape | |
| (zero Markov embeddings past n_out).""" | |
| s = self.s | |
| n = k if n_out is None else n_out | |
| if not 1 <= n <= k: | |
| raise ValueError(f"n_out {n} outside 1..{k}") | |
| rows = self._norm(hh, self.norm).view(B, k, s.hidden)[:, :n].reshape(B * n, s.hidden).to(torch.float16) | |
| l32 = torch.empty(B * n, s.vocab, dtype=torch.float32, device=self.dev) | |
| for c in range(0, B * n, self.HEAD_CHUNK): | |
| self.head(rows[c:c + self.HEAD_CHUNK], out=l32[c:c + self.HEAD_CHUNK]) | |
| logits = l32.to(torch.bfloat16).view(B, n, s.vocab) | |
| out = torch.zeros(B, k, dtype=torch.int64, device=self.dev) | |
| if self.fused and trace is None: | |
| for i in range(n): | |
| FU.markov_argmax_into(logits[:, i], anchors if i == 0 else out[:, i - 1], self.markov, out, i) | |
| emb = self.markov.embed(torch.cat((anchors.unsqueeze(1), out[:, :n - 1]), 1)) # [B, n, R] | |
| else: | |
| prev = anchors | |
| embs = [] | |
| for i in range(n): | |
| e = self.markov.embed(prev) | |
| li = logits[:, i] + self.markov(prev, e) | |
| d = li.argmax(dim=-1) | |
| if trace is not None: | |
| trace[f"markov{i}.logits"] = li | |
| out[:, i] = d | |
| embs.append(e) | |
| prev = d | |
| emb = torch.stack(embs, 1) | |
| if n < k: | |
| emb = torch.cat((emb, emb.new_zeros(B, k - n, emb.shape[-1])), 1) | |
| feat = torch.cat((hh.view(B, k, s.hidden), emb), dim=-1).float() | |
| conf = torch.sigmoid(feat @ self.conf_w) | |
| if n < k: | |
| conf[:, n:] = 0.0 | |
| if trace is not None: | |
| trace["base_logits"] = logits | |
| return out, conf | |
| def block_inputs(self, slots: torch.Tensor, anchors: torch.Tensor, last_pos: torch.Tensor, k: int, | |
| ctx_lo: torch.Tensor | None = None, gather: bool | None = None | |
| ) -> tuple[torch.Tensor, torch.Tensor, Window]: | |
| """Query ids/positions and the window: ring rows whose position is in [ctx_lo, last_pos]. ctx_lo defaults to | |
| max(last_pos - W + 1, 0); the server passes max(lo, anchor_pos - W) with the slot's contiguous lower bound. | |
| ``gather`` (default: not fused) also materializes the window rows and their mask.""" | |
| s = self.s | |
| ar = self.arange_block[:k] | |
| ids = torch.where(ar.unsqueeze(0) == 0, anchors.unsqueeze(1), torch.full_like(ar, s.noise_token).unsqueeze(0)) | |
| pos = torch.minimum(last_pos.unsqueeze(1) + 1 + ar.unsqueeze(0), self.q_clamp) | |
| if ctx_lo is None: | |
| ctx_lo = (last_pos - (s.window - 1)).clamp_min(0) | |
| win = Window(slots, ctx_lo, last_pos) | |
| if gather is None: | |
| gather = not self.fused | |
| if gather: | |
| cpos = self.ring_pos[slots] | |
| win.valid = (cpos >= ctx_lo.unsqueeze(1)) & (cpos <= last_pos.unsqueeze(1)) | |
| win.kv = self.ring_kv[slots] | |
| return ids, pos, win | |
| def draft(self, slots: torch.Tensor, anchors: torch.Tensor, last_pos: torch.Tensor, k: int, | |
| trace: dict | None = None, ctx_lo: torch.Tensor | None = None, n_out: int | None = None | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| """Eager draft step. slots/anchors/last_pos (/ctx_lo) int64 [B] on the device. The body always runs the | |
| whole k-token block; ``n_out`` limits the head / Markov / confidence to the first n_out positions.""" | |
| B = slots.shape[0] | |
| ids, pos, win = self.block_inputs(slots, anchors, last_pos, k, ctx_lo) | |
| hh = self.forward_block(ids, pos, win, trace) | |
| if trace is not None: | |
| trace["head_hidden"] = hh | |
| return self.heads(hh, anchors, B, k, trace, n_out) | |
| def set_query_clamp(self, max_model_len: int) -> None: | |
| if not 0 < max_model_len <= self.max_model_len: | |
| raise ValueError(f"max_model_len {max_model_len} outside 1..{self.max_model_len}") | |
| self.q_clamp.fill_(max_model_len - 1) | |
| def draft_with_margin(self, slots: torch.Tensor, anchors: torch.Tensor, last_pos: torch.Tensor, k: int, | |
| ctx_lo: torch.Tensor | None = None, n_out: int | None = None | |
| ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: | |
| """Eager draft that also returns top1 - top2 of (base + Markov) logits per position (CONTRACT WANT_MARGIN; | |
| 0 past n_out). Same drafts as draft(): the argmax runs on the same bf16 logits.""" | |
| tr: dict = {} | |
| ids, conf = self.draft(slots, anchors, last_pos, k, trace=tr, ctx_lo=ctx_lo, n_out=n_out) | |
| n = k if n_out is None else n_out | |
| mg = torch.zeros(slots.shape[0], k, dtype=torch.float32, device=self.dev) | |
| for i in range(n): | |
| v = tr[f"markov{i}.logits"].float().topk(2, dim=-1).values | |
| mg[:, i] = v[:, 0] - v[:, 1] | |
| return ids, conf, mg | |
| # ------------------------------------------------------------------------------------------ CUDA graphs | |
| def capture(self, batch_sizes=(1, 2, 3, 4), ks=(4, 5), insert_rows=(8, 16, 32, 64, 128), n_outs=(4, 5), | |
| margins: bool = True, pack_reply: bool = False) -> float: | |
| """One graph per (B, k, n_out) (n_out in n_outs and <= k; n_out = k always) for the draft step, the same with | |
| top1 - top2 margins (CONTRACT WANT_MARGIN; shadow mode asks for them) when ``margins``, and one per row | |
| bucket for the insert. ``pack_reply`` adds optional float64 reply twins; | |
| default False, so normal capture count and outputs remain unchanged. Returns seconds.""" | |
| t0 = time.time() | |
| dev = self.dev | |
| if self.pool is None: | |
| self.pool = torch.cuda.graph_pool_handle() | |
| bmax = max(batch_sizes) | |
| self.g_slots = torch.full((bmax,), self.n_slots, dtype=torch.int64, device=dev) | |
| self.g_anchor = torch.zeros(bmax, dtype=torch.int64, device=dev) | |
| self.g_last = torch.zeros(bmax, dtype=torch.int64, device=dev) | |
| self.g_lo = torch.zeros(bmax, dtype=torch.int64, device=dev) | |
| rmax = max(insert_rows) | |
| self.gi_slot = torch.full((rmax,), self.n_slots, dtype=torch.int64, device=dev) | |
| self.gi_pos = torch.zeros(rmax, dtype=torch.int64, device=dev) | |
| self.gi_taps = torch.zeros(rmax, self.s.hidden * len(self.s.taps), dtype=torch.bfloat16, device=dev) | |
| s1 = torch.cuda.Stream(dev) | |
| for B in batch_sizes: | |
| for k in ks: | |
| for n in sorted({x for x in n_outs if x <= k} | {k}): | |
| args = (self.g_slots[:B], self.g_anchor[:B], self.g_last[:B], k) | |
| lo = self.g_lo[:B] | |
| s1.wait_stream(torch.cuda.current_stream(dev)) | |
| with torch.cuda.stream(s1): | |
| for _ in range(2): | |
| self.draft(*args, ctx_lo=lo, n_out=n) # warm-up: kernel attributes, buffers, Triton JIT | |
| torch.cuda.current_stream(dev).wait_stream(s1) | |
| torch.cuda.synchronize(dev) | |
| gr = torch.cuda.CUDAGraph() | |
| # A process-wide default capture stream can belong to another device (e.g. TP2 rank 0). | |
| # Keep the pool on this model's stream, regardless of which model captured first. | |
| with torch.cuda.graph(gr, pool=self.pool, stream=s1): | |
| ids, conf = self.draft(*args, ctx_lo=lo, n_out=n) | |
| self.graphs[(B, k, n)] = {"graph": gr, "ids": ids, "conf": conf} | |
| if margins: | |
| s1.wait_stream(torch.cuda.current_stream(dev)) | |
| with torch.cuda.stream(s1): | |
| for _ in range(2): | |
| self.draft_with_margin(*args, ctx_lo=lo, n_out=n) | |
| torch.cuda.current_stream(dev).wait_stream(s1) | |
| torch.cuda.synchronize(dev) | |
| gr = torch.cuda.CUDAGraph() | |
| with torch.cuda.graph(gr, pool=self.pool, stream=s1): | |
| ids, conf, mg = self.draft_with_margin(*args, ctx_lo=lo, n_out=n) | |
| self.margin_graphs[(B, k, n)] = {"graph": gr, "ids": ids, "conf": conf, "mg": mg} | |
| if pack_reply: | |
| for margin in ((False, True) if margins else (False,)): | |
| gr = torch.cuda.CUDAGraph() | |
| with torch.cuda.graph(gr, pool=self.pool, stream=s1): | |
| values = (self.draft_with_margin(*args, ctx_lo=lo, n_out=n) if margin | |
| else self.draft(*args, ctx_lo=lo, n_out=n)) | |
| packed = torch.cat([v.to(torch.float64) for v in values], 1) | |
| self.reply_graphs[(B, k, n, margin)] = {"graph": gr, "packed": packed} | |
| for n in insert_rows: | |
| args = (self.gi_slot[:n], self.gi_pos[:n]) | |
| s1.wait_stream(torch.cuda.current_stream(dev)) | |
| with torch.cuda.stream(s1): | |
| for _ in range(2): | |
| self.insert(*args, self.main_x(self.gi_taps[:n])) | |
| torch.cuda.current_stream(dev).wait_stream(s1) | |
| torch.cuda.synchronize(dev) | |
| gr = torch.cuda.CUDAGraph() | |
| with torch.cuda.graph(gr, pool=self.pool, stream=s1): | |
| self.insert(*args, self.main_x(self.gi_taps[:n])) | |
| self.insert_graphs[n] = {"graph": gr} | |
| # the warm-ups wrote into the scratch slot only; keep it empty | |
| self.reset_slots(torch.tensor([self.n_slots], device=dev)) | |
| torch.cuda.synchronize(dev) | |
| return time.time() - t0 | |
| def has_graph(self, B: int, k: int, n_out: int | None = None, margin: bool = False) -> bool: | |
| return (B, k, k if n_out is None else n_out) in (self.margin_graphs if margin else self.graphs) | |
| def draft_graphed(self, slots: torch.Tensor, anchors: torch.Tensor, last_pos: torch.Tensor, k: int, | |
| ctx_lo: torch.Tensor | None = None, n_out: int | None = None, margin: bool = False, | |
| packed_reply: bool = False | |
| ) -> tuple[torch.Tensor, ...]: | |
| """Replay the (B, k, n_out) graph; returns views of the graph's output buffers (ids, conf[, margin]; copy | |
| before the next replay). ``margin``: the draft_with_margin graph (same ids and confidences). | |
| ``packed_reply`` returns a one-tuple holding the captured float64 wire staging tensor; | |
| requires capture(pack_reply=True), including a margin twin when margin=True.""" | |
| B = slots.shape[0] | |
| n = k if n_out is None else n_out | |
| g = (self.reply_graphs.get((B, k, n, margin)) if packed_reply | |
| else (self.margin_graphs if margin else self.graphs).get((B, k, n))) | |
| if g is None: | |
| raise KeyError(f"no CUDA graph for batch {B}, depth {k}, n_out {n}, margin {margin}, packed {packed_reply}") | |
| self.g_slots[:B].copy_(slots, non_blocking=True) | |
| self.g_anchor[:B].copy_(anchors, non_blocking=True) | |
| self.g_last[:B].copy_(last_pos, non_blocking=True) | |
| if ctx_lo is None: | |
| ctx_lo = (last_pos - (self.s.window - 1)).clamp_min(0) | |
| self.g_lo[:B].copy_(ctx_lo, non_blocking=True) | |
| g["graph"].replay() | |
| if packed_reply: | |
| return (g["packed"],) | |
| return (g["ids"], g["conf"], g["mg"]) if margin else (g["ids"], g["conf"]) | |
| def insert_taps_graphed(self, slot_rows: torch.Tensor, pos_rows: torch.Tensor, taps: torch.Tensor) -> None: | |
| """main_x + insert for n rows through the smallest captured bucket >= n (padding rows hit the scratch slot).""" | |
| n = slot_rows.shape[0] | |
| sizes = sorted(self.insert_graphs) | |
| b = next((x for x in sizes if x >= n), None) | |
| if b is None: | |
| raise ValueError(f"{n} insert rows exceed the largest graph bucket {sizes[-1]}") | |
| self.gi_slot[:n].copy_(slot_rows, non_blocking=True) | |
| self.gi_slot[n:b].fill_(self.n_slots) | |
| self.gi_pos[:n].copy_(pos_rows, non_blocking=True) | |
| self.gi_pos[n:b].zero_() | |
| self.gi_taps[:n].copy_(taps, non_blocking=True) | |
| self.gi_taps[n:b].zero_() # padding rows: no stale taps in main_proj's batch-wide prescale | |
| self.insert_graphs[b]["graph"].replay() | |
| # ------------------------------------------------------------------------------------------ accounting | |
| def memory_table(self) -> dict: | |
| def nb(t: torch.Tensor) -> int: | |
| return t.numel() * t.element_size() | |
| st_dense = sum(sum(l.nbytes() for l in (st.wq_a, st.wkv, st.wq_b, st.wo_b, st.sh_w1, st.sh_w3, st.sh_w2)) | |
| for st in self.stages) | |
| st_woa = sum(nb(st.wo_a8) + nb(st.wo_a_sc) + (nb(st.wo_a_t) if st.wo_a_t is not None else 0) | |
| for st in self.stages) | |
| st_exp = sum(sum(nb(t) for t in st.bank.t.values()) for st in self.stages) | |
| st_small = sum(nb(t) for st in self.stages for t in (st.attn_sink, st.q_norm, st.kv_norm, st.attn_norm, | |
| st.ffn_norm, st.gate_w, st.gate_bias, *st.hc_attn, | |
| *st.hc_ffn)) | |
| return { | |
| "embed_bf16": nb(self.embed), | |
| "head_exl3_k6": self.head.nbytes(), | |
| "main_proj_exl3_k5": self.main_proj.nbytes(), | |
| "stages_dense_exl3_k5": st_dense, | |
| "stages_wo_a_fp8": st_woa, | |
| "stages_experts_exl3_k2": st_exp, | |
| "stages_norms_hc_gate_fp32": st_small, | |
| "markov_w1_w2_bf16": nb(self.markov.w1) + nb(self.markov.w2), | |
| "markov_cache_rows_bf16": nb(self.markov.cache) + nb(self.markov.slot_of_token), | |
| "confidence_and_norms": nb(self.conf_w) + nb(self.norm) + nb(self.main_norm), | |
| "rope_cos_sin_fp32": nb(self.cos_sin), | |
| "windows_per_slot": nb(self.ring_kv[0]) + nb(self.ring_pos[0]), | |
| "windows_total": nb(self.ring_kv) + nb(self.ring_pos), | |
| "coop_scratch": sum(nb(t) for t in (self.coop.had_g, self.coop.had_u, self.coop.gu_g, self.coop.gu_u, | |
| self.coop.act_out, self.coop.d_out, self.coop.ctr, *self.coop.out)), | |
| "torch_allocated": torch.cuda.memory_allocated(self.dev), | |
| "torch_reserved": torch.cuda.memory_reserved(self.dev), | |
| "torch_max_allocated": torch.cuda.max_memory_allocated(self.dev), | |
| } | |