metrale_gpu_runtime/buffers/sizes.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-25: Byte size of every buffer in the GPU buffer arena, from the model config, the serve limits and four environment levers.
4//!
5//! Owner: gpu-runtime.
6//! Invariants:
7//! - [`BufferSizes::from_config`] allocates nothing; besides its arguments it
8//! reads only `METRALE_GGUF_NATIVE_Q2`, `METRALE_GGUF_NATIVE_Q2_MMQ`,
9//! `METRALE_FP8_ROWWISE` and, through `attn_splitk::policy_from_env`,
10//! `METRALE_ATTN_DECODE_SPLITK`.
11
12use metrale_config::ModelConfig;
13use metrale_kernels::attn_splitk;
14
15mod regions;
16mod total;
17
18/// 2026-09-25: The widest `M` the fused dense-FFN gate+up decode GEMM serves, and so
19/// the row extent of `ffn_gate_up_fused`. Declared here because the arena is sized
20/// in this crate; the dispatch (`layers/dense_ffn_gateup_fused.rs` in
21/// metrale-model-layers) reads it for its `5..=GATEUP_FUSED_MAX_M` band, so the band
22/// and the buffer cannot disagree.
23pub const GATEUP_FUSED_MAX_M: usize = 16;
24
25/// 2026-09-29: Most rows (`R = Σ ks`) one batched MTP verify scores: 128 sequences at one
26/// draft (the serve's widest batch, `DECODE_META_MAX_ROWS`). It sizes the verify block-table
27/// rows (`sizes/regions.rs`) and the logits floor here; model-engine's `VERIFY_ROW_CAP` and
28/// the scheduler's `VERIFY_ROW_BUDGET` are this value. Was 160 (32 sequences at up to five
29/// rows).
30pub const VERIFY_ROW_CAP: usize = 256;
31
32/// 2026-09-25: Byte size of each arena buffer. `M` is `max_batch_tokens` and
33/// `ceil16(M)` is M rounded up to a multiple of 16.
34#[derive(Debug, Clone)]
35pub struct BufferSizes {
36 pub hidden_states: usize,
37 pub residual: usize,
38 pub norm_output: usize,
39 pub qkv_output: usize,
40 pub attn_output: usize,
41 pub gate_logits: usize,
42 /// 2026-09-25: FP32 router logits `[M, num_experts + zero_expert_num]` for
43 /// the `METRALE_FP32_GATE` and `METRALE_FP32_ROUTING` paths, which run the
44 /// top-K on unrounded logits. 256 bytes for a model without experts.
45 pub gate_logits_f32: usize,
46 /// 2026-09-25: FP32 MoE-input norm output `[M, hidden]` that the router GEMM
47 /// reads under `METRALE_FP32_ROUTING`. 256 bytes for a model without experts.
48 pub moe_router_in_f32: usize,
49 pub moe_output: usize,
50 pub logits: usize,
51 pub ssm_qkvz: usize,
52 pub ssm_ba: usize,
53 pub ssm_deinterleaved: usize,
54 pub ssm_gates: usize,
55 pub ssm_conv_out_f32: usize,
56 pub scratch: usize,
57 pub expert_gate_out: usize,
58 pub expert_up_out: usize,
59 pub expert_down_out: usize,
60 pub splitk_workspace: usize,
61 /// 2026-09-25: GDN FLA chunked-prefill scratch, W | U | S | uc | gc back to
62 /// back. 0 unless the linear-attention key and value head dims are both 128,
63 /// the only shape the FLA prefill kernels run.
64 pub gdn_fla_scratch: usize,
65 /// 2026-09-25: Mamba-2 SSD chunked-scan scratch, dt | dA_cumsum | CB. 0
66 /// unless the model has Mamba-2 heads and a state size.
67 pub ssd_scratch: usize,
68 /// 2026-09-25: Grouped O-projection latent `[M, o_groups * o_lora_rank]`
69 /// BF16, at least 256 bytes.
70 pub o_latent: usize,
71 /// 2026-09-25: The zero weight `norm_unit_w`, `max(hidden, Mamba-2 d_inner)`
72 /// BF16 values.
73 pub norm_unit_w: usize,
74 /// 2026-09-25: Hyper-connection residual streams `[M, hc_mult, hidden]`,
75 /// FP32. 256 bytes when `hc_mult == 0`.
76 pub hc_streams: usize,
77 /// 2026-09-25: Hyper-connection `post` weights `[M, hc_mult]` F32.
78 pub hc_post: usize,
79 /// 2026-09-25: Hyper-connection `comb` matrix `[M, hc_mult, hc_mult]` F32.
80 pub hc_comb: usize,
81 /// 2026-09-25: Low-rank hyper-connection scratch: the larger of the decode
82 /// split layout (T <= 64, FP32) and the prefill GEMM layout (slabs of at
83 /// most 2048 tokens, BF16); see the sizing in `from_config`. 256 bytes
84 /// without a low-rank hyper-connection.
85 pub hc_lowrank_scratch: usize,
86 /// 2026-09-25: QSA prefill-selection scratch for 2048-row slabs, shared by
87 /// the indexer layers: qk `[2048, (index_n_heads + 1) * index_head_dim]`
88 /// BF16, q_post `[2048, index_n_heads, index_head_dim]` F32, scores
89 /// `[2048, ceil(max_seq_len / index_compress_ratio)]` F32, lists
90 /// `[2048, index_topk / index_compress_ratio]` i32. 256 bytes without an
91 /// indexer. `layers/qsa_select.rs` carves the same layout.
92 pub qsa_select_scratch: usize,
93 /// 2026-09-25: Token ids `[M]` u32 of the current pass, read by the
94 /// DeepSeek-V4 hash-routed MoE layers (`tid2eid[token_id]`). At least 256
95 /// bytes.
96 pub token_ids: usize,
97 /// 2026-09-25: Dense-FFN activation-quant scratch shared by every layer's
98 /// prefill, sized for K = max(hidden, intermediate); all 0 for a MoE model.
99 /// `ffn_act_q8`: q8_1_mmq activations for the Q4_K MMQ path.
100 /// `ffn_act_a`: int8 `[ceil16(M), K]`, also NVFP4 packed and FP8 activations.
101 /// `ffn_act_scale`: `[ceil16(M), K/32]` 4-byte int8 scales, also the NVFP4
102 /// and FP8 scales.
103 pub ffn_act_q8: usize,
104 pub ffn_act_a: usize,
105 pub ffn_act_scale: usize,
106 /// 2026-09-25: `[K/128, ceil16(M)]` f32 copy of the FP8 activation scales,
107 /// token index contiguous, for the cuBLASLt block-scaled FFN GEMM; the
108 /// quantizer writes `[M, K/128]` into `ffn_act_scale`. 0 for a MoE model.
109 pub ffn_act_scale_kmajor: usize,
110 /// 2026-09-25: `[ceil16(GATEUP_FUSED_MAX_M), 2 * intermediate]` BF16 output of
111 /// the fused dense-FFN gate+up decode GEMM; a row is `[gate | up]`. A buffer
112 /// of its own because the fused arm serves only the decode band, and a
113 /// widened `expert_gate_out` would carry `max_batch_tokens` rows. Sized for
114 /// every dense model, whatever the lever says: the levers are resolved above
115 /// this crate. 0 for a MoE model.
116 pub ffn_gate_up_fused: usize,
117 /// 2026-09-25: FP8 activation scratch for the prefill projections, 1 byte per
118 /// element, `[ceil16(M), K]` for the widest K among hidden,
119 /// `q_heads * head_dim`, Mamba-2 d_inner and the GDN value dim.
120 pub fp8_act: usize,
121 /// 2026-09-25: The grouped FP8 MoE scratch slab (`moe_fp8_scratch::Layout`); 0 for a
122 /// dense model.
123 pub moe_fp8_scratch: usize,
124 /// 2026-09-25: One f32 scale per 128 elements of `fp8_act`.
125 pub fp8_act_scale: usize,
126 /// 2026-09-25: `[K/128, ceil16(M)]` f32 transpose of `fp8_act_scale`, token
127 /// index contiguous, for the cuBLASLt block-scaled projections. Sized for
128 /// every model, the SSM `in_proj_qkvz` arm included.
129 pub fp8_act_scale_kmajor: usize,
130 /// 2026-09-25: LoRA shrink output `xa = x @ Aᵀ`, `[M, adapter_max_rank]`
131 /// BF16. This and the other `lora_*` entries are 0 when
132 /// `adapter_max_rank == 0`.
133 pub lora_xa: usize,
134 /// 2026-09-25: LoRA expand output `delta = xa @ Bᵀ`, `[M, n]` BF16 with n the
135 /// widest target: max(hidden, intermediate, the q projection width).
136 pub lora_delta: usize,
137 /// 2026-09-25: LoRA hidden-activation scratch `[M, intermediate_size]` BF16.
138 pub lora_hact: usize,
139 /// 2026-09-25: LoRA adapter slot per prefill token, `[M]` i32. A buffer of
140 /// its own, so it cannot overlap the metadata regions of `scratch`.
141 pub lora_seq_slot: usize,
142 /// 2026-09-25: Keep-packed Q2_0 prefill dequant scratch
143 /// (`METRALE_GGUF_NATIVE_Q2=1`): one BF16 buffer for the largest keep-packed
144 /// projection, reused by each projection's dequant. Sized by
145 /// [`crate::buffers::q2_dequant_scratch_bytes`]; 0 unless the lever is `1`.
146 pub q2_dequant_scratch: usize,
147 /// 2026-09-25: Keep-packed Q2_0 MMQ prefill q8_1 activation scratch
148 /// (`METRALE_GGUF_NATIVE_Q2_MMQ=1`), shared by every keep-packed projection:
149 /// each quantizes its BF16 activation here and runs the packed MMQ GEMM.
150 /// 0 unless the lever is `1`.
151 pub q2_act_q8: usize,
152 /// 2026-09-25: Row-wise FP8 GDN prefill BF16-weight slab
153 /// (`METRALE_FP8_ROWWISE=1`): every GDN layer's BF16 `in_proj_qkvz` and
154 /// `out_proj`, one slice pair per layer carved on its first prefill and kept
155 /// for the arena's life. Sized in `sizes_rowwise.rs`; 0 unless the lever is
156 /// `1`.
157 pub ssm_rowwise_w_bf16: usize,
158}
159
160impl BufferSizes {
161 /// 2026-09-25: Every buffer size, in bytes. `max_seq_len` and `kv_block_size`
162 /// set the block-table width in `scratch` (`max_seq_len / kv_block_size + 1`
163 /// blocks, 256 when `kv_block_size` is 0) and the QSA score width;
164 /// `max_batch_size` sets the decode-metadata rows.
165 pub fn from_config(
166 config: &ModelConfig,
167 max_batch_tokens: usize,
168 max_seq_len: usize,
169 kv_block_size: usize,
170 max_batch_size: usize,
171 ) -> Self {
172 let decode_meta = super::DecodeMetaLayout::for_max_batch_size(max_batch_size);
173 let bf16 = 2;
174 let m = max_batch_tokens;
175 let h = config.hidden_size;
176
177 // 2026-09-25: A gated q projection (`attn_gated`) writes [Q | gate], twice
178 // `q_heads * head_dim` wide.
179 let q_heads = config.num_attention_heads;
180 let kv_heads = config.num_key_value_heads;
181 let hd = config.head_dim;
182 let q_proj_mul = if config.attn_gated { 2 } else { 1 };
183 let qkv_dim = (q_heads * q_proj_mul + 2 * kv_heads) * hd;
184
185 let scratch = regions::scratch_bytes(config, m, max_seq_len, kv_block_size, &decode_meta);
186
187 // 2026-09-25: The row extent of every buffer a cuBLASLt block-scaled FP8
188 // GEMM touches. `ops::cublas_fp8_proj_prequant` (metrale-model-layers)
189 // hands cuBLASLt `ceil16(M)` rows: the pad rows of the activation and its
190 // scales are read, and the pad rows of the output are written, so a
191 // buffer sized for M rows would be overrun by up to 15 rows.
192 let m_pad = m.div_ceil(16) * 16;
193 // 2026-09-25: Expert outputs: `ceil16(max(M, 3))` rows of top_k experts
194 // (or of the dense FFN).
195 let k_max = m.max(3).div_ceil(16) * 16;
196 let expert_inter = if config.num_experts > 0 {
197 let routed = config.num_experts_per_tok * config.moe_intermediate_size;
198 k_max * routed.max(config.intermediate_size)
199 } else {
200 k_max * config.intermediate_size
201 };
202 let expert_gate_out = expert_inter * bf16;
203 let expert_up_out = expert_inter * bf16;
204 // 2026-09-25: A LatentMoE model's routed experts write `moe_latent_size`
205 // wide rows (`moe_input_size`).
206 let moe_out_dim = config.moe_input_size();
207 let expert_down_out = if config.num_experts > 0 {
208 k_max * config.num_experts_per_tok * moe_out_dim * bf16
209 } else {
210 k_max * h * bf16
211 };
212
213 // 2026-09-25: Logit rows: min(M, max(VERIFY_ROW_CAP, decode rows + 1)):
214 // `VERIFY_ROW_CAP` is the most rows a batched MTP verify scores. The mixed
215 // decode step (`decode_b2.rs`) writes a prefill row after its `padded_n`
216 // decode rows, and `padded_n` can reach `decode_meta.rows()`, hence + 1.
217 let logits_tokens = m.min(VERIFY_ROW_CAP.max(decode_meta.rows() + 1));
218
219 // 2026-09-25: Mamba-2 d_inner may exceed hidden_size; norm_output and
220 // attn_output hold rows of either.
221 let mamba2_d_inner = config.mamba2_d_inner();
222 let max_dim = h.max(mamba2_d_inner);
223
224 // 2026-09-25: Split-K decode workspace: one `[o[head_dim], m, l]` F32 slot
225 // per (sequence, q head, split). A short workspace is an out-of-bounds
226 // device write, so the slot count comes from `attn_splitk::workspace_slots`
227 // under the policy the dispatch resolves too (`policy_from_env`), for
228 // `decode_meta.rows()` sequences, the widest batch the metadata upload
229 // accepts.
230 let splitk_slots = attn_splitk::workspace_slots(
231 attn_splitk::policy_from_env(),
232 metrale_kernels::TARGET_SM_COUNT,
233 q_heads as u32,
234 decode_meta.rows() as u32,
235 (max_batch_size as u32).max(1),
236 ) as usize;
237 let splitk_workspace = splitk_slots * (hd + 2) * 4;
238
239 let residual_elem = bf16;
240
241 // 2026-09-25: The widest K of the prefill projections that quantize into
242 // `fp8_act`: hidden (qkv, ssm qkvz), `q_heads * head_dim` (o_proj), Mamba-2
243 // d_inner (its out_proj) and the GDN value dim (its out_proj).
244 let max_proj_k = h
245 .max(q_heads * hd)
246 .max(mamba2_d_inner)
247 .max(config.linear_num_value_heads * config.linear_value_head_dim);
248 let fp8_act = m_pad * max_proj_k;
249 let fp8_act_scale = m_pad * max_proj_k.div_ceil(128) * 4;
250 // 2026-09-25: The transpose is written from `fp8_act_scale` while that is
251 // still live, so the two cannot share a buffer.
252 let fp8_act_scale_kmajor = fp8_act_scale;
253 // 2026-09-25: LoRA scratch, only when `adapter_max_rank > 0`. `max_n` is
254 // the widest projection output an adapter can target.
255 let (lora_xa, lora_delta, lora_hact, lora_seq_slot) = if config.adapter_max_rank > 0 {
256 let max_n = h
257 .max(config.intermediate_size)
258 .max(q_proj_mul * q_heads * hd);
259 (
260 m * config.adapter_max_rank * bf16,
261 m * max_n * bf16,
262 m * config.intermediate_size * bf16,
263 m * 4,
264 )
265 } else {
266 (0, 0, 0, 0)
267 };
268
269 let (ssd_scratch, gdn_fla_scratch) = regions::ssm_scratch_bytes(config, m, bf16);
270
271 let (q2_dequant_scratch, q2_act_q8) = super::sizes_q2::q2_scratch_sizes(config, m, h, hd);
272
273 let ssm_rowwise_w_bf16 = super::sizes_rowwise::ssm_rowwise_w_bf16_bytes(config);
274
275 // 2026-09-25: `ceil16` rows for the same cuBLASLt pad as `m_pad`.
276 let ffn_gate_up_fused = if config.num_experts == 0 {
277 let rows = GATEUP_FUSED_MAX_M.div_ceil(16) * 16;
278 rows * 2 * config.intermediate_size * bf16
279 } else {
280 0
281 };
282
283 let (ffn_act_q8, ffn_act_a, ffn_act_scale, ffn_act_scale_kmajor) =
284 if config.num_experts == 0 {
285 let kmax = h.max(config.intermediate_size);
286 let kpad = kmax.div_ceil(256) * 256;
287 // 2026-09-25: The q8_1 term is `q8_1_scratch_bytes` in
288 // metrale-model-layers (`ops/q4k_mmq.rs`). The int8 activation
289 // `[ceil16(M), K]` and scale `[ceil16(M), K/32]` terms also hold
290 // the NVFP4 (`[M, K/2]`, `[M, K/16]`) and FP8 (`[M, K]`,
291 // `[M, K/128]`) forms.
292 (
293 m * kpad * 4 + (1 << 20),
294 m_pad * kmax,
295 m_pad * (kmax / 32) * 4,
296 m_pad * (kmax / 128) * 4,
297 )
298 } else {
299 (0, 0, 0, 0)
300 };
301
302 Self {
303 hidden_states: m * h * residual_elem,
304 residual: m * h * residual_elem,
305 norm_output: m * max_dim * bf16,
306 // 2026-09-25: `m_pad` rows: the cuBLASLt Q/K/V prefill arm
307 // (`prefill_qkv_w8a8.rs`) writes `ceil16(M)` rows of Q here.
308 qkv_output: m_pad * qkv_dim * bf16,
309 attn_output: (m * config.num_attention_heads * config.head_dim * bf16)
310 .max(m * mamba2_d_inner * bf16)
311 // 2026-09-25: Absorbed MLA writes [M, q_heads, kv_lora_rank + rope dim].
312 .max(if config.kv_lora_rank > 0 {
313 m * config.num_attention_heads
314 * (config.kv_lora_rank + config.qk_rope_head_dim)
315 * bf16
316 } else {
317 0
318 }),
319 gate_logits: if config.num_experts > 0 {
320 // 2026-09-25: The router also scores the `zero_expert_num` zero
321 // experts, which have no FFN.
322 m * (config.num_experts + config.zero_expert_num) * bf16
323 } else {
324 256
325 },
326 gate_logits_f32: if config.num_experts > 0 {
327 m * (config.num_experts + config.zero_expert_num) * 4
328 } else {
329 256
330 },
331 moe_router_in_f32: if config.num_experts > 0 {
332 m * h * 4
333 } else {
334 256
335 },
336 // 2026-09-25: `ceil16(M)` rows: the cuBLASLt dense-FFN down
337 // projection writes its output here.
338 moe_output: m.div_ceil(16) * 16 * h * bf16,
339 logits: logits_tokens * config.vocab_size * bf16,
340 // 2026-09-25: The SSM buffers are also scratch for other layers, so
341 // each is the largest of its uses and at least 256 bytes. `ssm_qkvz`
342 // holds the QKVZ projection (`ceil16(M)` rows for its cuBLASLt arm),
343 // the Mamba-2 in_proj output, prefill K and V, and the MoE shared-expert
344 // up output.
345 ssm_qkvz: (m_pad * config.ssm_qkvz_size() * bf16)
346 .max(m * config.mamba2_in_proj_size() * bf16)
347 // 2026-09-25: K at row 0 and V at row M, V `ceil16(M)` rows tall
348 // on the cuBLASLt arm (`prefill_qkv_w8a8.rs`).
349 .max((m + m_pad) * kv_heads * hd * bf16)
350 .max(m * config.shared_expert_intermediate_size * bf16)
351 .max(256),
352 ssm_ba: (m * config.ssm_ba_size() * bf16)
353 .max(m * config.moe_latent_size * bf16)
354 // 2026-09-25: MLA uses `ssm_ba` for q_latent [M, q_lora_rank] and
355 // later for the K rope rows [M, qk_rope_head_dim], one at a time.
356 .max(if config.kv_lora_rank > 0 {
357 (m * config.qk_rope_head_dim * bf16).max(m * config.q_lora_rank * bf16)
358 } else {
359 0
360 })
361 // 2026-09-29: The MTP batched propose's `[n, 2 * hidden]` concat, for a propose
362 // as wide as the serve's widest batch (`DECODE_META_MAX_ROWS`, at most M): below
363 // that the propose splits into groups, each reading the drafter's experts again.
364 .max(m.min(super::DECODE_META_MAX_ROWS) * 2 * h * bf16)
365 .max(256),
366 // 2026-09-25: `ceil16(M)` rows as in `ssm_qkvz`: on a
367 // `sequential_qkvz` model the QKVZ projection writes here.
368 ssm_deinterleaved: (m_pad * config.ssm_qkvz_size() * bf16)
369 .max(m * config.mamba2_d_xbc() * bf16)
370 .max(m * q_heads * hd * bf16)
371 // 2026-09-25: Absorbed MLA's Q [M, q_heads, kv_lora_rank + rope dim].
372 .max(if config.kv_lora_rank > 0 {
373 m * q_heads * (config.kv_lora_rank + config.qk_rope_head_dim) * bf16
374 } else {
375 0
376 })
377 .max(256),
378 // 2026-09-29: Also the MTP batched propose's normed hidden rows `[n, hidden]` BF16,
379 // n up to `DECODE_META_MAX_ROWS`, at most M (see `ssm_ba`).
380 ssm_gates: (m * config.linear_num_value_heads * 2 * 4)
381 .max(m.min(super::DECODE_META_MAX_ROWS) * h * bf16)
382 .max(256),
383 // 2026-09-25: FP32 conv1d output, bounded by the QKVZ width. MLA also
384 // uses it for its Q rope rows [M, q_heads * qk_rope_head_dim] BF16.
385 ssm_conv_out_f32: (m * config.ssm_qkvz_size() * 4)
386 .max(if config.kv_lora_rank > 0 {
387 m * q_heads * config.qk_rope_head_dim * bf16
388 } else {
389 0
390 })
391 .max(256),
392 scratch,
393 expert_gate_out,
394 expert_up_out,
395 expert_down_out,
396 splitk_workspace,
397 gdn_fla_scratch,
398 ssd_scratch,
399 o_latent: (m * config.o_groups * config.o_lora_rank * bf16).max(256),
400 norm_unit_w: max_dim * bf16,
401 hc_streams: if config.hc_mult > 0 {
402 m * config.hc_mult * h * 4
403 } else {
404 256
405 },
406 hc_post: if config.hc_mult > 0 {
407 (m * config.hc_mult * 4).max(256)
408 } else {
409 256
410 },
411 hc_comb: if config.hc_mult > 0 {
412 (m * config.hc_mult * config.hc_mult * 4).max(256)
413 } else {
414 256
415 },
416 hc_lowrank_scratch: if config.hc_mult > 0 && config.hc_lowrank > 0 {
417 // 2026-09-25: Two layouts share this region
418 // (`hyper_connection_lowrank.rs`, `hyper_connection_lowrank_gemm.rs`):
419 // - T <= 64: normed FP32 [64, hc*H], then low FP32 [64, rank];
420 // - T > 64, in slabs of Ts <= 2048 tokens: normed BF16 [Ts, hc*H],
421 // up_pre BF16 [Ts, hc*H], low BF16 [Ts, rank], inj_pre BF16 [Ts, hc].
422 let t = m.min(64);
423 let split = t * (config.hc_mult * h + config.hc_lowrank) * 4;
424 let ts = m.min(2048);
425 let gemm = ts * (2 * config.hc_mult * h + config.hc_lowrank + config.hc_mult) * 2;
426 split.max(gemm)
427 } else {
428 256
429 },
430 qsa_select_scratch: if config.index_topk > 0 && config.index_compress_ratio > 0 {
431 const ROWS: usize = 2048;
432 let qkw = (config.index_n_heads + 1) * config.index_head_dim;
433 let n_blocks = max_seq_len.div_ceil(config.index_compress_ratio);
434 let topk = config.index_topk / config.index_compress_ratio;
435 ROWS * qkw * 2
436 + ROWS * config.index_n_heads * config.index_head_dim * 4
437 + ROWS * n_blocks * 4
438 + ROWS * topk * 4
439 } else {
440 256
441 },
442 token_ids: (m * 4).max(256),
443 ffn_act_q8,
444 ffn_act_a,
445 ffn_act_scale,
446 ffn_act_scale_kmajor,
447 ffn_gate_up_fused,
448 fp8_act,
449 moe_fp8_scratch: super::moe_fp8_scratch::Layout::new(config, m).bytes,
450 fp8_act_scale,
451 fp8_act_scale_kmajor,
452 lora_xa,
453 lora_delta,
454 lora_hact,
455 lora_seq_slot,
456 q2_dequant_scratch,
457 q2_act_q8,
458 ssm_rowwise_w_bf16,
459 }
460 }
461}