1#![cfg(not(target_arch = "wasm32"))]
36
37use super::*;
38
39const SLOT: wgpu::BufferAddress = 256;
41const RESIDENT_LOGITS_CHUNK: usize = 131_072;
44
45const GEMM_SLOTS_PER_LAYER: u64 = 7;
47const ATTN_SLOTS_PER_LAYER: u64 = 3;
49
50const ELEM_SLOT_RMS: wgpu::BufferAddress = 0;
52const ELEM_SLOT_ADD: wgpu::BufferAddress = SLOT;
53const ELEM_SLOT_SILU: wgpu::BufferAddress = SLOT * 2;
54const ELEM_SLOTS: u64 = 3;
55
56struct LayerBinds {
58 rms_attn: wgpu::BindGroup,
59 triple_qkv: Option<wgpu::BindGroup>,
61 dual_kv: Option<wgpu::BindGroup>,
63 k_gemm: wgpu::BindGroup,
64 k_write: wgpu::BindGroup,
65 v_gemm: wgpu::BindGroup,
66 v_write: wgpu::BindGroup,
67 q_gemm: wgpu::BindGroup,
69 q: wgpu::BindGroup,
71 o_resid: wgpu::BindGroup,
73 rms_ffn: wgpu::BindGroup,
74 fused_ffn: Option<wgpu::BindGroup>,
76 gate: Option<wgpu::BindGroup>,
77 up: Option<wgpu::BindGroup>,
78 silu: Option<wgpu::BindGroup>,
79 down_resid: wgpu::BindGroup,
81}
82
83fn fused_ffn_quant_supported(ggml_type: u32) -> bool {
85 use crate::ggml_quants::{
86 GGML_TYPE_Q4_0, GGML_TYPE_Q4_K, GGML_TYPE_Q4_K_SOA, GGML_TYPE_Q5_0, GGML_TYPE_Q6_K,
87 GGML_TYPE_Q8_0,
88 };
89 matches!(
90 ggml_type,
91 GGML_TYPE_Q4_0
92 | GGML_TYPE_Q5_0
93 | GGML_TYPE_Q8_0
94 | GGML_TYPE_Q4_K
95 | GGML_TYPE_Q4_K_SOA
96 | GGML_TYPE_Q6_K
97 )
98}
99
100struct OutChunk {
102 gemm: wgpu::BindGroup,
103 topk: wgpu::BindGroup,
104 rows: u32,
105 cand_count: usize,
106}
107
108struct LayerProtos {
111 k_write: AttentionGpuParams,
112 v_write: AttentionGpuParams,
113 q: AttentionGpuParams,
114}
115
116pub(crate) struct ResidentDecodePlan {
117 key: (u64, u64, u32),
119 n_embd: usize,
120 n_ffn: usize,
121 kv_dim: usize,
122 q_dim: usize,
123 n_head: u32,
124 n_kv_head: u32,
125 layout: KvCacheLayout,
126 layer_protos: Vec<LayerProtos>,
127 layers: Vec<LayerBinds>,
128 rms_out: wgpu::BindGroup,
129 out_chunks: Vec<OutChunk>,
130 total_cands: usize,
131 attn_dyn_arena: wgpu::Buffer,
133 dyn_scratch: Vec<u8>,
135 hidden_a: wgpu::Buffer,
137 normed: wgpu::Buffer,
139 staging: wgpu::Buffer,
141 hidden_staging: wgpu::Buffer,
143 use_coop: bool,
144 use_multirow: bool,
146 use_warp: bool,
148 use_ffn_mr: bool,
150 use_ffn_warp: bool,
152 #[allow(dead_code)]
155 use_fused_ffn: bool,
156 dispatches_per_token: u32,
158 readback_bytes_per_token: u32,
160}
161
162pub(crate) enum ResidentDecodeState {
163 Unbuilt,
164 Ineligible(u64),
166 Ready(Box<ResidentDecodePlan>),
167}
168
169enum ResidentTokenOutcome {
171 Argmax(StreamingArgmaxResult),
172 HiddenReady,
173}
174
175impl QTensorEngine {
176 pub fn resident_dispatches_per_token(&self) -> Option<u32> {
178 match &self.resident_decode {
179 ResidentDecodeState::Ready(plan) => Some(plan.dispatches_per_token),
180 _ => None,
181 }
182 }
183
184 pub fn resident_readback_bytes_per_token(&self) -> Option<u32> {
186 match &self.resident_decode {
187 ResidentDecodeState::Ready(plan) => Some(plan.readback_bytes_per_token),
188 _ => None,
189 }
190 }
191
192 fn plan_key(&self, index: &crate::gguf_sharder::GgufTensorIndex) -> (u64, u64, u32) {
193 let base = self
194 .gguf_mmap
195 .as_deref()
196 .map(|m| m.as_ptr() as u64)
197 .unwrap_or(0);
198 (base, index.tensor_data_start, index.hyperparams.n_layer)
199 }
200
201 pub fn dispatch_token_forward_resident(
204 &mut self,
205 index: &crate::gguf_sharder::GgufTensorIndex,
206 emb: &[f32],
207 token_idx: u32,
208 ) -> Option<StreamingArgmaxResult> {
209 match self.with_resident_plan(index, |this, plan| {
210 this.run_resident_token(plan, emb, token_idx, None)
211 })? {
212 Some(ResidentTokenOutcome::Argmax(a)) => Some(a),
213 _ => None,
214 }
215 }
216
217 pub fn dispatch_token_forward_resident_hidden(
225 &mut self,
226 index: &crate::gguf_sharder::GgufTensorIndex,
227 emb: &[f32],
228 token_idx: u32,
229 out_hidden: &mut [f32],
230 ) -> bool {
231 matches!(
232 self.with_resident_plan(index, |this, plan| {
233 this.run_resident_token(plan, emb, token_idx, Some(out_hidden))
234 }),
235 Some(Some(ResidentTokenOutcome::HiddenReady))
236 )
237 }
238
239 fn with_resident_plan<R>(
240 &mut self,
241 index: &crate::gguf_sharder::GgufTensorIndex,
242 f: impl FnOnce(&Self, &mut ResidentDecodePlan) -> R,
243 ) -> Option<R> {
244 if !crate::llm_bench::resident_decode_enabled()
245 || !crate::llm_bench::resident_weights_enabled()
246 || crate::llm_bench::cpu_attention_enabled()
247 || crate::llm_bench::kv_dict_enabled()
248 {
249 return None;
250 }
251 let key = self.plan_key(index);
255 let state = std::mem::replace(&mut self.resident_decode, ResidentDecodeState::Unbuilt);
256 let mut plan = match state {
257 ResidentDecodeState::Ready(p) if p.key == key => p,
258 ResidentDecodeState::Ineligible(k) if k == (key.0 ^ key.1) => {
259 self.resident_decode = ResidentDecodeState::Ineligible(k);
260 return None;
261 }
262 _ => match self.build_resident_plan(index, key) {
263 Some(p) => p,
264 None => {
265 wlog("[resident-decode] plan build ineligible — legacy path");
266 self.resident_decode = ResidentDecodeState::Ineligible(key.0 ^ key.1);
267 return None;
268 }
269 },
270 };
271 let result = f(self, &mut plan);
272 self.resident_decode = ResidentDecodeState::Ready(plan);
273 Some(result)
274 }
275
276 fn run_resident_token(
277 &self,
278 plan: &mut ResidentDecodePlan,
279 emb: &[f32],
280 token_idx: u32,
281 out_hidden: Option<&mut [f32]>,
282 ) -> Option<ResidentTokenOutcome> {
283 let n_embd = plan.n_embd;
284 if emb.len() < n_embd || token_idx >= plan.layout.max_context {
285 return None;
286 }
287 let queue = self.gpu_queue();
288
289 queue.write_buffer(&plan.hidden_a, 0, bytemuck::cast_slice(&emb[..n_embd]));
292
293 let (mask_words, mask_active) =
294 crate::compute_universe::attention_kv_mask_u32(token_idx, plan.layout.max_context);
295 if mask_active != 0 {
296 queue.write_buffer(
297 self.attention_mask_buf.as_ref()?,
298 0,
299 bytemuck::cast_slice(&mask_words),
300 );
301 }
302
303 let ap_size = std::mem::size_of::<AttentionGpuParams>();
304 for (l, protos) in plan.layer_protos.iter().enumerate() {
305 let base = l as u64 * ATTN_SLOTS_PER_LAYER * SLOT;
306 let mut k = protos.k_write;
307 k.token_idx = token_idx;
308 k.batch_start_token_idx = token_idx;
309 let mut v = protos.v_write;
310 v.token_idx = token_idx;
311 v.batch_start_token_idx = token_idx;
312 let mut q = protos.q;
313 q.token_idx = token_idx;
314 q.batch_start_token_idx = token_idx;
315 q.mask_active = mask_active;
316 q.mask_word_count = if mask_active != 0 {
317 KV_ATTENTION_MASK_WORDS as u32
318 } else {
319 0
320 };
321 for (slot, p) in [(0u64, &k), (1, &v), (2, &q)] {
322 let off = (base + slot * SLOT) as usize;
323 plan.dyn_scratch[off..off + ap_size].copy_from_slice(bytemuck::bytes_of(p));
324 }
325 }
326 queue.write_buffer(&plan.attn_dyn_arena, 0, &plan.dyn_scratch);
327
328 let mut encoder =
332 self.gpu_device()
333 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
334 label: Some("ResidentTokenEncoder"),
335 });
336 let rms = self.elem_gpu_pipeline(ELEM_OP_RMS_NORM)?;
337 let silu = &self.elem_silu_mul_pipeline;
338 let attn = &self.attention_pipeline;
339 let sample_path = out_hidden.is_some();
340 let topk = if sample_path {
341 None
342 } else {
343 Some(self.output_topk_pipeline.as_ref()?)
344 };
345
346 let gemv_proj_wg = |n_out: u32| {
349 if plan.use_coop {
350 n_out
351 } else {
352 n_out.div_ceil(64)
353 }
354 };
355 let gemv_large_wg = |n_out: u32| {
356 if plan.use_coop && plan.use_multirow {
357 crate::llm_bench::coop_gemv_workgroups(n_out)
358 } else if plan.use_coop {
359 n_out
360 } else {
361 n_out.div_ceil(64)
362 }
363 };
364 let elem_wg = |n: u32| n.div_ceil(64);
365 let (n_embd_u, n_ffn_u) = (n_embd as u32, plan.n_ffn as u32);
366 let kv_dim_u = plan.kv_dim as u32;
367 let q_dim_u = plan.q_dim as u32;
368
369 {
370 let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
372 label: Some("ResidentFullToken"),
373 timestamp_writes: crate::llm_gpu_profiler::pass_writes_both(),
374 });
375 const FFN_MR_ROWS: u32 = 4;
377 let (fused_pipe, fused_wg) = if plan.use_coop && plan.use_ffn_mr {
378 (
379 &self.ffn_fused_mr_pipeline,
380 n_ffn_u.div_ceil(FFN_MR_ROWS).max(1),
381 )
382 } else if plan.use_coop && plan.use_ffn_warp {
383 (&self.ffn_fused_warp_pipeline, n_ffn_u)
384 } else if plan.use_coop {
385 (&self.ffn_fused_coop_pipeline, n_ffn_u)
386 } else {
387 (&self.ffn_fused_pipeline, n_ffn_u.div_ceil(64))
388 };
389 let gemv_proj = if plan.use_coop {
390 &self.coop_gemv_pipeline
391 } else {
392 &self.pipeline
393 };
394 let gemv_large = if plan.use_coop && plan.use_multirow {
395 &self.coop_gemv_mr_pipeline
396 } else if plan.use_coop && plan.use_warp {
397 &self.coop_gemv_warp_pipeline
398 } else if plan.use_coop {
399 &self.coop_gemv_pipeline
400 } else {
401 &self.pipeline
402 };
403 let gemv_resid = if plan.use_multirow {
404 &self.coop_gemv_residual_mr_pipeline
405 } else if plan.use_warp {
406 &self.coop_gemv_residual_warp_pipeline
407 } else {
408 &self.coop_gemv_residual_pipeline
409 };
410 for lb in &plan.layers {
411 cpass.set_pipeline(rms);
413 cpass.set_bind_group(0, &lb.rms_attn, &[]);
414 cpass.dispatch_workgroups(1, 1, 1);
415 if let Some(ref tri) = lb.triple_qkv {
417 cpass.set_pipeline(&self.triple_gemv_pipeline);
418 cpass.set_bind_group(0, tri, &[]);
419 cpass.dispatch_workgroups(q_dim_u.max(1), 1, 1);
420 cpass.set_pipeline(attn);
421 cpass.set_bind_group(0, &lb.k_write, &[]);
422 cpass.dispatch_workgroups(plan.n_kv_head.max(1), 1, 1);
423 cpass.set_pipeline(attn);
424 cpass.set_bind_group(0, &lb.v_write, &[]);
425 cpass.dispatch_workgroups(plan.n_kv_head.max(1), 1, 1);
426 } else if let Some(ref dual) = lb.dual_kv {
427 let dual_mr = matches!(
430 std::env::var("QUALIA_LLM_DUAL_MR").ok().as_deref(),
431 Some("1") | Some("true")
432 );
433 const DUAL_ROWS: u32 = 4;
434 if dual_mr {
435 cpass.set_pipeline(&self.dual_gemv_mr_pipeline);
436 cpass.set_bind_group(0, dual, &[]);
437 cpass.dispatch_workgroups(kv_dim_u.div_ceil(DUAL_ROWS).max(1), 1, 1);
438 } else {
439 cpass.set_pipeline(&self.dual_gemv_pipeline);
440 cpass.set_bind_group(0, dual, &[]);
441 cpass.dispatch_workgroups(kv_dim_u.max(1), 1, 1);
442 }
443 cpass.set_pipeline(attn);
444 cpass.set_bind_group(0, &lb.k_write, &[]);
445 cpass.dispatch_workgroups(plan.n_kv_head.max(1), 1, 1);
446 cpass.set_pipeline(attn);
447 cpass.set_bind_group(0, &lb.v_write, &[]);
448 cpass.dispatch_workgroups(plan.n_kv_head.max(1), 1, 1);
449 cpass.set_pipeline(gemv_proj);
450 cpass.set_bind_group(0, &lb.q_gemm, &[]);
451 cpass.dispatch_workgroups(gemv_proj_wg(q_dim_u), 1, 1);
452 } else {
453 cpass.set_pipeline(gemv_proj);
454 cpass.set_bind_group(0, &lb.k_gemm, &[]);
455 cpass.dispatch_workgroups(gemv_proj_wg(kv_dim_u), 1, 1);
456 cpass.set_pipeline(attn);
457 cpass.set_bind_group(0, &lb.k_write, &[]);
458 cpass.dispatch_workgroups(plan.n_kv_head.max(1), 1, 1);
459 cpass.set_pipeline(gemv_proj);
460 cpass.set_bind_group(0, &lb.v_gemm, &[]);
461 cpass.dispatch_workgroups(gemv_proj_wg(kv_dim_u), 1, 1);
462 cpass.set_pipeline(attn);
463 cpass.set_bind_group(0, &lb.v_write, &[]);
464 cpass.dispatch_workgroups(plan.n_kv_head.max(1), 1, 1);
465 cpass.set_pipeline(gemv_proj);
466 cpass.set_bind_group(0, &lb.q_gemm, &[]);
467 cpass.dispatch_workgroups(gemv_proj_wg(q_dim_u), 1, 1);
468 }
469 cpass.set_pipeline(attn);
471 cpass.set_bind_group(0, &lb.q, &[]);
472 cpass.dispatch_workgroups(plan.n_head.max(1), 1, 1);
473 cpass.set_pipeline(gemv_resid);
474 cpass.set_bind_group(0, &lb.o_resid, &[]);
475 cpass.dispatch_workgroups(gemv_large_wg(n_embd_u), 1, 1);
476 cpass.set_pipeline(rms);
477 cpass.set_bind_group(0, &lb.rms_ffn, &[]);
478 cpass.dispatch_workgroups(1, 1, 1);
479 if let Some(ref fbg) = lb.fused_ffn {
481 cpass.set_pipeline(fused_pipe);
482 cpass.set_bind_group(0, fbg, &[]);
483 cpass.dispatch_workgroups(fused_wg, 1, 1);
484 } else if let (Some(g), Some(u), Some(s)) =
485 (lb.gate.as_ref(), lb.up.as_ref(), lb.silu.as_ref())
486 {
487 for (pipe, bg, wg_x) in [
488 (gemv_large, g, gemv_large_wg(n_ffn_u)),
489 (gemv_large, u, gemv_large_wg(n_ffn_u)),
490 (silu, s, elem_wg(n_ffn_u)),
491 ] {
492 cpass.set_pipeline(pipe);
493 cpass.set_bind_group(0, bg, &[]);
494 cpass.dispatch_workgroups(wg_x, 1, 1);
495 }
496 }
497 cpass.set_pipeline(gemv_resid);
499 cpass.set_bind_group(0, &lb.down_resid, &[]);
500 cpass.dispatch_workgroups(gemv_large_wg(n_embd_u), 1, 1);
501 }
502 cpass.set_pipeline(rms);
504 cpass.set_bind_group(0, &plan.rms_out, &[]);
505 cpass.dispatch_workgroups(1, 1, 1);
506
507 if !sample_path {
512 let topk = topk?;
513 let logits_mr = plan.use_coop && plan.out_chunks.iter().any(|c| c.rows > 60_000);
514 let logits_pipe = if logits_mr {
515 &self.coop_gemv_mr_pipeline
516 } else {
517 gemv_large
518 };
519 let logits_wg = |rows: u32| {
520 if logits_mr {
521 crate::llm_bench::coop_gemv_workgroups(rows)
522 } else {
523 gemv_large_wg(rows)
524 }
525 };
526 for chunk in &plan.out_chunks {
527 cpass.set_pipeline(logits_pipe);
528 cpass.set_bind_group(0, &chunk.gemm, &[]);
529 cpass.dispatch_workgroups(logits_wg(chunk.rows), 1, 1);
530 cpass.set_pipeline(topk);
531 cpass.set_bind_group(0, &chunk.topk, &[]);
532 cpass.dispatch_workgroups(chunk.cand_count as u32, 1, 1);
533 }
534 }
535 drop(cpass);
536 crate::llm_gpu_profiler::resolve(&mut encoder);
537
538 if sample_path {
539 let hb = (n_embd * 4) as wgpu::BufferAddress;
540 encoder.copy_buffer_to_buffer(&plan.normed, 0, &plan.hidden_staging, 0, hb);
541 } else {
542 let cand_val = self.topk_cand_val_buf.as_ref()?;
543 let cand_idx = self.topk_cand_idx_buf.as_ref()?;
544 let cand_bytes = (plan.total_cands * 4) as wgpu::BufferAddress;
546 encoder.copy_buffer_to_buffer(cand_val, 0, &plan.staging, 0, cand_bytes);
547 encoder.copy_buffer_to_buffer(cand_idx, 0, &plan.staging, cand_bytes, cand_bytes);
548 }
549 }
550
551 queue.submit(Some(encoder.finish()));
553 crate::llm_gpu_profiler::accumulate(crate::llm_gpu_profiler::Phase::FusedBlock);
554
555 if let Some(out) = out_hidden {
556 if out.len() < n_embd {
557 return None;
558 }
559 let map_bytes = (n_embd * 4) as wgpu::BufferAddress;
560 let slice = plan.hidden_staging.slice(..map_bytes);
561 let (tx, rx) = futures_channel::oneshot::channel();
562 slice.map_async(wgpu::MapMode::Read, move |r| {
563 let _ = tx.send(r);
564 });
565 self.poll_wait();
566 let mapped_ok = if let Ok(handle) = tokio::runtime::Handle::try_current() {
567 handle.block_on(rx).ok().map(|m| m.is_ok()).unwrap_or(false)
568 } else {
569 false
570 };
571 if !mapped_ok {
572 let _ = plan.hidden_staging.unmap();
573 return None;
574 }
575 {
576 let data = slice
577 .get_mapped_range()
578 .expect("wgpu buffer map_range failed");
579 let floats: &[f32] = bytemuck::cast_slice(&data[..n_embd * 4]);
580 out[..n_embd].copy_from_slice(floats);
581 }
582 plan.hidden_staging.unmap();
583 return Some(ResidentTokenOutcome::HiddenReady);
584 }
585
586 let map_bytes = (plan.total_cands * 8) as wgpu::BufferAddress;
587 let slice = plan.staging.slice(..map_bytes);
588 let (tx, rx) = futures_channel::oneshot::channel();
589 slice.map_async(wgpu::MapMode::Read, move |r| {
590 let _ = tx.send(r);
591 });
592 self.poll_wait();
593 let mapped_ok = if let Ok(handle) = tokio::runtime::Handle::try_current() {
594 handle.block_on(rx).ok().map(|m| m.is_ok()).unwrap_or(false)
595 } else {
596 false
597 };
598 if !mapped_ok {
599 let _ = plan.staging.unmap();
600 return None;
601 }
602
603 let mut best_token_id = 0u32;
604 let mut max_logit = f32::NEG_INFINITY;
605 {
606 let data = slice
607 .get_mapped_range()
608 .expect("wgpu buffer map_range failed");
609 let val_bytes = plan.total_cands * 4;
610 let vals: &[f32] = bytemuck::cast_slice(&data[..val_bytes]);
611 let idxs: &[u32] = bytemuck::cast_slice(&data[val_bytes..val_bytes * 2]);
612 let mut offset = 0usize;
613 let mut row_start = 0u32;
614 for chunk in &plan.out_chunks {
615 for i in 0..chunk.cand_count {
616 let pos = offset + i;
617 let v = vals[pos];
618 let token_id = row_start + idxs[pos];
619 if v > f32::NEG_INFINITY
620 && (v > max_logit || (v == max_logit && token_id < best_token_id))
621 {
622 max_logit = v;
623 best_token_id = token_id;
624 }
625 }
626 offset += chunk.cand_count;
627 row_start += chunk.rows;
628 }
629 }
630 plan.staging.unmap();
631
632 if max_logit == f32::NEG_INFINITY {
633 None
634 } else {
635 Some(ResidentTokenOutcome::Argmax(StreamingArgmaxResult {
636 best_token_id,
637 max_logit,
638 }))
639 }
640 }
641
642 fn build_resident_plan(
646 &mut self,
647 index: &crate::gguf_sharder::GgufTensorIndex,
648 key: (u64, u64, u32),
649 ) -> Option<Box<ResidentDecodePlan>> {
650 let mmap_arc = self.gguf_mmap.clone()?;
651 let mmap: &[u8] = &mmap_arc;
652 let h = index.hyperparams;
653 let layout = self.kv_layout?;
654 let n_embd = h.n_embd as usize;
655 let n_layer = h.n_layer;
656 let n_head = h.n_head as usize;
657 let n_kv = h.effective_n_kv_head() as usize;
658 let head_dim = h.head_dim() as usize;
659 let q_dim = n_head * head_dim;
660 let kv_dim = n_kv * head_dim;
661 if n_layer == 0
662 || n_embd == 0
663 || n_embd > MAX_HIDDEN_DIM
664 || q_dim == 0
665 || kv_dim == 0
666 || self.output_topk_pipeline.is_none()
667 || self.attention_mask_buf.is_none()
668 {
669 return None;
670 }
671
672 let logits_buf = self.mc8_logits_resident_buf.clone()?;
675 let logits_row_bytes = self.mc8_logits_row_bytes as u64;
676 let logits_info = *index.logits_projection_info()?;
677 let (logits_in, vocab) = Self::matmul_dims(&logits_info);
678 if logits_in != n_embd
679 || vocab == 0
680 || !ggml_gpu_gemm_supported(logits_info.ggml_type)
681 || logits_row_bytes == 0
682 {
683 return None;
684 }
685 let out_norm_info = *index.output_norm_info()?;
686
687 let use_coop = crate::llm_bench::coop_gemv_enabled();
688 let device = self.gpu_device().clone();
689 let mk_storage = |label: &str, floats: usize| {
690 device.create_buffer(&wgpu::BufferDescriptor {
691 label: Some(label),
692 size: ((floats * 4 + 255) & !255).max(4) as wgpu::BufferAddress,
693 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
694 mapped_at_creation: false,
695 })
696 };
697 let mk_storage_copy_src = |label: &str, floats: usize| {
699 device.create_buffer(&wgpu::BufferDescriptor {
700 label: Some(label),
701 size: ((floats * 4 + 255) & !255).max(4) as wgpu::BufferAddress,
702 usage: wgpu::BufferUsages::STORAGE
703 | wgpu::BufferUsages::COPY_DST
704 | wgpu::BufferUsages::COPY_SRC,
705 mapped_at_creation: false,
706 })
707 };
708 let n_ffn = {
709 let t0 = index.get_layer_tensors(0);
710 let gate = t0.ffn_gate.as_ref()?;
711 Self::matmul_dims(gate).1
712 };
713 if n_ffn == 0 || n_ffn > MAX_STACK_GEMM_DIM {
714 return None;
715 }
716
717 let hidden_a = mk_storage("ResidentHiddenA", n_embd);
718 let hidden_b = mk_storage("ResidentHiddenB", n_embd);
719 let normed = mk_storage_copy_src("ResidentNormed", n_embd);
720 let attn_out = mk_storage("ResidentAttnOut", q_dim.max(n_embd));
721 let k_proj = mk_storage("ResidentKProj", kv_dim);
724 let v_proj = mk_storage("ResidentVProj", kv_dim);
725 let q_proj = mk_storage("ResidentQProj", q_dim);
727 let gate_buf = mk_storage("ResidentGate", n_ffn);
728 let up_buf = mk_storage("ResidentUp", n_ffn);
729 let silu_buf = mk_storage("ResidentSilu", n_ffn);
730 let logits_chunk = mk_storage("ResidentLogitsChunk", RESIDENT_LOGITS_CHUNK);
731
732 let norm_stride = ((n_embd * 4 + 255) & !255) as wgpu::BufferAddress;
735 let norm_res = device.create_buffer(&wgpu::BufferDescriptor {
736 label: Some("ResidentNormWeights"),
737 size: norm_stride * (2 * n_layer as u64 + 1),
738 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
739 mapped_at_creation: false,
740 });
741 let queue = self.gpu_queue().clone();
742 let upload_norm = |slot: u64, info: &GgufTensorInfo| -> bool {
743 let mut w = [0f32; MAX_HIDDEN_DIM];
744 if dequant_norm_row_into(mmap, index.tensor_data_start, info, &mut w) < n_embd {
745 return false;
746 }
747 queue.write_buffer(
748 &norm_res,
749 slot * norm_stride,
750 bytemuck::cast_slice(&w[..n_embd]),
751 );
752 true
753 };
754
755 let full_chunks = vocab.div_ceil(RESIDENT_LOGITS_CHUNK);
758 let gemm_slots = n_layer as u64 * GEMM_SLOTS_PER_LAYER + full_chunks as u64;
759 let static_arena = device.create_buffer(&wgpu::BufferDescriptor {
760 label: Some("ResidentStaticParams"),
761 size: (gemm_slots + ELEM_SLOTS + full_chunks as u64) * SLOT,
762 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
763 mapped_at_creation: false,
764 });
765 let elem_base = gemm_slots * SLOT;
766 let topk_base = (gemm_slots + ELEM_SLOTS) * SLOT;
767
768 let attn_dyn_arena = device.create_buffer(&wgpu::BufferDescriptor {
769 label: Some("ResidentAttnDynParams"),
770 size: n_layer as u64 * ATTN_SLOTS_PER_LAYER * SLOT,
771 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
772 mapped_at_creation: false,
773 });
774
775 let block_size = crate::topk::TOPK_BLOCK_SIZE;
777 let total_cands = vocab.div_ceil(block_size);
778 let staging = device.create_buffer(&wgpu::BufferDescriptor {
779 label: Some("ResidentTopkStaging"),
780 size: ((total_cands * 8).max(8)) as wgpu::BufferAddress,
781 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
782 mapped_at_creation: false,
783 });
784 let hidden_staging = device.create_buffer(&wgpu::BufferDescriptor {
785 label: Some("ResidentHiddenStaging"),
786 size: ((n_embd * 4).max(16)) as wgpu::BufferAddress,
787 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
788 mapped_at_creation: false,
789 });
790
791 let elem_p = |n: usize, op: u32| ElemGpuParams {
793 n: n as u32,
794 batch: 1,
795 op,
796 eps: RMS_NORM_EPS,
797 a_row_stride: 0,
798 b_row_stride: 0,
799 out_row_stride: 0,
800 a_slot: 0,
801 b_slot: 0,
802 out_slot: 0,
803 _pad: 0,
804 };
805 queue.write_buffer(
806 &static_arena,
807 elem_base + ELEM_SLOT_RMS,
808 bytemuck::bytes_of(&elem_p(n_embd, ELEM_OP_RMS_NORM)),
809 );
810 queue.write_buffer(
811 &static_arena,
812 elem_base + ELEM_SLOT_ADD,
813 bytemuck::bytes_of(&elem_p(n_embd, ELEM_OP_ADD_RESIDUAL)),
814 );
815 queue.write_buffer(
816 &static_arena,
817 elem_base + ELEM_SLOT_SILU,
818 bytemuck::bytes_of(&elem_p(n_ffn, ELEM_OP_SILU_MUL)),
819 );
820
821 let gemm_layout = self.native_gemm_bind_layout(use_coop).clone();
823 let attn_layout = self.attention_bind_layout.clone();
824 let rms_pipe = self.elem_gpu_pipeline(ELEM_OP_RMS_NORM)?.clone();
825 let rms_layout = rms_pipe.get_bind_group_layout(0);
826 let silu_layout = self.elem_silu_mul_bind_layout.clone();
827 let topk_layout = self.output_topk_bind_layout.clone()?;
828 let mask_buf = self.attention_mask_buf.as_ref()?.clone();
829 let kv_buf = self.kv_cache_gpu.as_ref()?.clone();
830 let cand_val = self.topk_cand_val_buf.as_ref()?.clone();
831 let cand_idx = self.topk_cand_idx_buf.as_ref()?.clone();
832
833 let gp_sz = std::num::NonZeroU64::new(std::mem::size_of::<GemmGpuParams>() as u64);
834 let ap_sz = std::num::NonZeroU64::new(std::mem::size_of::<AttentionGpuParams>() as u64);
835 let ep_sz = std::num::NonZeroU64::new(std::mem::size_of::<ElemGpuParams>() as u64);
836 let tp_sz = std::num::NonZeroU64::new(16);
837 fn ubind(
838 buf: &wgpu::Buffer,
839 off: wgpu::BufferAddress,
840 sz: Option<std::num::NonZeroU64>,
841 ) -> wgpu::BindingResource<'_> {
842 wgpu::BindingResource::Buffer(wgpu::BufferBinding {
843 buffer: buf,
844 offset: off,
845 size: sz,
846 })
847 }
848 let norm_bind = |slot: u64| {
849 wgpu::BindingResource::Buffer(wgpu::BufferBinding {
850 buffer: &norm_res,
851 offset: slot * norm_stride,
852 size: std::num::NonZeroU64::new((n_embd * 4) as u64),
853 })
854 };
855 let mk_elem_bg = |label: &str,
856 layout: &wgpu::BindGroupLayout,
857 a: wgpu::BindingResource,
858 b: wgpu::BindingResource,
859 out: &wgpu::Buffer,
860 p_off: wgpu::BufferAddress| {
861 device.create_bind_group(&wgpu::BindGroupDescriptor {
862 label: Some(label),
863 layout,
864 entries: &[
865 wgpu::BindGroupEntry {
866 binding: 0,
867 resource: a,
868 },
869 wgpu::BindGroupEntry {
870 binding: 1,
871 resource: b,
872 },
873 wgpu::BindGroupEntry {
874 binding: 2,
875 resource: out.as_entire_binding(),
876 },
877 wgpu::BindGroupEntry {
878 binding: 3,
879 resource: ubind(&static_arena, elem_base + p_off, ep_sz),
880 },
881 ],
882 })
883 };
884 let mk_gemm_bg = |label: &str,
885 input: &wgpu::Buffer,
886 weight: wgpu::BindingResource,
887 p_slot: u64,
888 out: &wgpu::Buffer| {
889 device.create_bind_group(&wgpu::BindGroupDescriptor {
891 label: Some(label),
892 layout: &gemm_layout,
893 entries: &[
894 wgpu::BindGroupEntry {
895 binding: 0,
896 resource: input.as_entire_binding(),
897 },
898 wgpu::BindGroupEntry {
899 binding: 1,
900 resource: weight,
901 },
902 wgpu::BindGroupEntry {
903 binding: 2,
904 resource: ubind(&static_arena, p_slot * SLOT, gp_sz),
905 },
906 wgpu::BindGroupEntry {
907 binding: 3,
908 resource: out.as_entire_binding(),
909 },
910 wgpu::BindGroupEntry {
911 binding: 4,
912 resource: input.as_entire_binding(),
913 },
914 ],
915 })
916 };
917 let resid_layout = &self.coop_gemv_residual_bind_layout;
918 let mk_resid_bg = |label: &str,
919 input: &wgpu::Buffer,
920 weight: wgpu::BindingResource,
921 p_slot: u64,
922 residual: &wgpu::Buffer,
923 out: &wgpu::Buffer| {
924 device.create_bind_group(&wgpu::BindGroupDescriptor {
925 label: Some(label),
926 layout: resid_layout,
927 entries: &[
928 wgpu::BindGroupEntry {
929 binding: 0,
930 resource: input.as_entire_binding(),
931 },
932 wgpu::BindGroupEntry {
933 binding: 1,
934 resource: weight,
935 },
936 wgpu::BindGroupEntry {
937 binding: 2,
938 resource: ubind(&static_arena, p_slot * SLOT, gp_sz),
939 },
940 wgpu::BindGroupEntry {
941 binding: 3,
942 resource: out.as_entire_binding(),
943 },
944 wgpu::BindGroupEntry {
945 binding: 4,
946 resource: residual.as_entire_binding(),
947 },
948 ],
949 })
950 };
951 let layer_f32s = layout.layer_stride as u64;
952 let mk_attn_bg = |label: &str,
953 input: &wgpu::Buffer,
954 weight: &wgpu::Buffer,
955 dyn_slot: u64,
956 layer: u32,
957 out: &wgpu::Buffer| {
958 let kv_binding = wgpu::BufferBinding {
959 buffer: &kv_buf,
960 offset: layer as u64 * layer_f32s * 4,
961 size: std::num::NonZeroU64::new((layer_f32s * 4).max(4)),
962 };
963 device.create_bind_group(&wgpu::BindGroupDescriptor {
964 label: Some(label),
965 layout: &attn_layout,
966 entries: &[
967 wgpu::BindGroupEntry {
968 binding: 0,
969 resource: input.as_entire_binding(),
970 },
971 wgpu::BindGroupEntry {
972 binding: 1,
973 resource: weight.as_entire_binding(),
974 },
975 wgpu::BindGroupEntry {
976 binding: 2,
977 resource: ubind(&attn_dyn_arena, dyn_slot * SLOT, ap_sz),
978 },
979 wgpu::BindGroupEntry {
980 binding: 3,
981 resource: wgpu::BindingResource::Buffer(kv_binding),
982 },
983 wgpu::BindGroupEntry {
984 binding: 4,
985 resource: out.as_entire_binding(),
986 },
987 wgpu::BindGroupEntry {
988 binding: 5,
989 resource: mask_buf.as_entire_binding(),
990 },
991 ],
992 })
993 };
994
995 let gemm_params =
996 |ggml_type: u32, n_in: usize, n_out: usize, row_elems: u32, raw_len: usize| {
997 GemmGpuParams {
998 n_in: n_in as u32,
999 n_out: n_out as u32,
1000 weight_ggml_type: ggml_type,
1001 weight_row_elems: row_elems,
1002 weight_byte_len: raw_len as u32,
1003 n_batch: 1,
1004 in_row_stride: 0,
1005 out_row_stride: 0,
1006 }
1007 };
1008
1009 let mut layers = Vec::with_capacity(n_layer as usize);
1010 let mut layer_protos = Vec::with_capacity(n_layer as usize);
1011 let mut layer_gate_ggml_type: u32 = 0;
1012 for l in 0..n_layer {
1013 let t = index.get_layer_tensors(l);
1014 let (q_info, k_info, v_info) = (t.attn_q?, t.attn_k?, t.attn_v?);
1015 let o_info = t.attn_output?;
1016 let (gate_info, up_info, down_info) = (t.ffn_gate?, t.ffn_up?, t.ffn_down?);
1017 if l == 0 {
1018 layer_gate_ggml_type = gate_info.ggml_type;
1019 }
1020 let attn_norm = t.attn_norm?;
1021 let ffn_norm = t.ffn_norm?;
1022 for i in [&q_info, &k_info, &v_info] {
1023 if !ggml_gpu_attention_shader_supported(i.ggml_type)
1024 || !ggml_gpu_gemm_supported(i.ggml_type)
1025 {
1026 return None;
1027 }
1028 }
1029 for i in [&o_info, &gate_info, &up_info, &down_info] {
1030 if !ggml_gpu_gemm_supported(i.ggml_type) {
1031 return None;
1032 }
1033 }
1034 let (q_in, q_out) = Self::matmul_dims(&q_info);
1036 let (k_in, k_out) = Self::matmul_dims(&k_info);
1037 let (v_in, v_out) = Self::matmul_dims(&v_info);
1038 let (o_in, o_out) = Self::matmul_dims(&o_info);
1039 let (g_in, g_out) = Self::matmul_dims(&gate_info);
1040 let (u_in, u_out) = Self::matmul_dims(&up_info);
1041 let (d_in, d_out) = Self::matmul_dims(&down_info);
1042 if q_in != n_embd
1043 || q_out != q_dim
1044 || k_in != n_embd
1045 || v_in != n_embd
1046 || k_out != kv_dim
1047 || v_out != kv_dim
1048 || o_in != q_dim
1049 || o_out != n_embd
1050 || g_in != n_embd
1051 || u_in != n_embd
1052 || g_out != n_ffn
1053 || u_out != n_ffn
1054 || d_in != n_ffn
1055 || d_out != n_embd
1056 {
1057 return None;
1058 }
1059 let fetch = |i: &GgufTensorInfo| {
1060 crate::ggml_quants::fetch_tensor_bytes(mmap, index.tensor_data_start, i).ok()
1061 };
1062 let (q_raw, k_raw, v_raw) = (fetch(&q_info)?, fetch(&k_info)?, fetch(&v_info)?);
1063 let (o_raw, g_raw, u_raw, d_raw) = (
1064 fetch(&o_info)?,
1065 fetch(&gate_info)?,
1066 fetch(&up_info)?,
1067 fetch(&down_info)?,
1068 );
1069 #[cfg(all(not(target_arch = "wasm32"), feature = "cuda"))]
1073 if crate::prefer_tensor_core_gemm()
1074 && matches!(
1075 std::env::var("QUALIA_LLM_CUDA_DECODE").ok().as_deref(),
1076 Some("1") | Some("true")
1077 )
1078 {
1079 use crate::ggml_quants::GGML_TYPE_Q4_K_SOA;
1080 if l == 0 {
1081 if let Some(layout) = self.kv_layout.as_ref() {
1082 if !layout.int8 && layout.dict_k == 0 {
1083 let _ = crate::ensure_device_kv_cache(
1084 layout.max_context,
1085 layout.n_layer,
1086 layout.n_kv_head,
1087 layout.head_dim,
1088 layout.slot_kv_elems,
1089 layout.layer_stride,
1090 layout.total_f32_elems,
1091 );
1092 }
1093 }
1094 }
1095 let mut pack: Vec<(&[u8], usize, usize)> = Vec::with_capacity(7);
1096 if q_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1097 pack.push((q_raw, q_in, q_out));
1098 }
1099 if k_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1100 pack.push((k_raw, n_embd, kv_dim));
1101 }
1102 if v_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1103 pack.push((v_raw, n_embd, kv_dim));
1104 }
1105 if o_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1106 pack.push((o_raw, o_in, o_out));
1107 }
1108 if gate_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1109 pack.push((g_raw, g_in, g_out));
1110 }
1111 if up_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1112 pack.push((u_raw, u_in, u_out));
1113 }
1114 if down_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1115 pack.push((d_raw, d_in, d_out));
1116 }
1117 if !pack.is_empty() {
1118 let _ = crate::preload_q4k_soa_weights(&pack);
1119 }
1120 let tc_prewarm = matches!(
1124 std::env::var("QUALIA_LLM_CUDA_TC_PREWARM").ok().as_deref(),
1125 Some("1") | Some("true")
1126 );
1127 if tc_prewarm {
1128 let mut warmed = 0u32;
1129 let mut try_warm = |info: &crate::gguf_sharder::GgufTensorInfo,
1130 raw: &[u8],
1131 n_in: usize,
1132 n_out: usize| {
1133 if QTensorEngine::prewarm_cuda_weight(info, raw, n_in, n_out) {
1134 warmed += 1;
1135 }
1136 };
1137 if q_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1138 try_warm(&q_info, q_raw, q_in, q_out);
1139 }
1140 if k_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1141 try_warm(&k_info, k_raw, n_embd, kv_dim);
1142 }
1143 if v_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1144 try_warm(&v_info, v_raw, n_embd, kv_dim);
1145 }
1146 if o_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1147 try_warm(&o_info, o_raw, o_in, o_out);
1148 }
1149 if gate_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1150 try_warm(&gate_info, g_raw, g_in, g_out);
1151 }
1152 if up_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1153 try_warm(&up_info, u_raw, u_in, u_out);
1154 }
1155 if down_info.ggml_type == GGML_TYPE_Q4_K_SOA {
1156 try_warm(&down_info, d_raw, d_in, d_out);
1157 }
1158 if l == 0 && warmed > 0 {
1159 log::info!(
1160 "LLM_LOAD|cuda_tc_prewarm|layer0|dense_entries={}|cache_len={}",
1161 warmed,
1162 crate::weight_cache_len()
1163 );
1164 }
1165 }
1166 }
1167 let res = |raw: &[u8]| self.resident_weight_buffer(raw.as_ptr() as u64, raw);
1168 let (q_w, k_w, v_w) = (res(q_raw)?, res(k_raw)?, res(v_raw)?);
1169 let o_w = res(o_raw)?;
1170 let ffn_bind =
1172 |info: &GgufTensorInfo, raw: &[u8]| -> Option<(wgpu::Buffer, u32, u32, u32)> {
1173 if let Some(p) = self.promote_matrix_to_f16_resident(info, raw) {
1174 return Some(p);
1175 }
1176 let b = res(raw)?;
1177 Some((b, info.ggml_type, raw.len() as u32, info.dims[0] as u32))
1178 };
1179 let (g_w, g_ty, g_blen, g_row) = ffn_bind(&gate_info, g_raw)?;
1180 let (u_w, u_ty, u_blen, u_row) = ffn_bind(&up_info, u_raw)?;
1181 let (d_w, d_ty, d_blen, d_row) = ffn_bind(&down_info, d_raw)?;
1182 if l == 0
1183 && (g_ty == crate::ggml_quants::GGML_TYPE_F16
1184 || u_ty == crate::ggml_quants::GGML_TYPE_F16
1185 || d_ty == crate::ggml_quants::GGML_TYPE_F16)
1186 {
1187 log::info!(
1188 "LLM_LOAD|ffn-f16|promoted gate/up/down (types g={} u={} d={})",
1189 g_ty,
1190 u_ty,
1191 d_ty
1192 );
1193 }
1194
1195 if !upload_norm(2 * l as u64, &attn_norm) || !upload_norm(2 * l as u64 + 1, &ffn_norm) {
1196 return None;
1197 }
1198
1199 let gbase = l as u64 * GEMM_SLOTS_PER_LAYER;
1201 for (i, (ggml_type, n_in, n_out, row_elems, raw_len)) in [
1202 (
1203 k_info.ggml_type,
1204 n_embd,
1205 kv_dim,
1206 k_info.dims[0] as u32,
1207 k_raw.len(),
1208 ),
1209 (
1210 v_info.ggml_type,
1211 n_embd,
1212 kv_dim,
1213 v_info.dims[0] as u32,
1214 v_raw.len(),
1215 ),
1216 (
1217 q_info.ggml_type,
1218 n_embd,
1219 q_dim,
1220 q_info.dims[0] as u32,
1221 q_raw.len(),
1222 ),
1223 (
1224 o_info.ggml_type,
1225 q_dim,
1226 n_embd,
1227 o_info.dims[0] as u32,
1228 o_raw.len(),
1229 ),
1230 (g_ty, n_embd, n_ffn, g_row, g_blen as usize),
1231 (u_ty, n_embd, n_ffn, u_row, u_blen as usize),
1232 (d_ty, n_ffn, n_embd, d_row, d_blen as usize),
1233 ]
1234 .into_iter()
1235 .enumerate()
1236 {
1237 queue.write_buffer(
1238 &static_arena,
1239 (gbase + i as u64) * SLOT,
1240 bytemuck::bytes_of(&gemm_params(ggml_type, n_in, n_out, row_elems, raw_len)),
1241 );
1242 }
1243
1244 let mut k_p = Self::attention_gpu_params(
1246 &h,
1247 &layout,
1248 l,
1249 0,
1250 &k_info,
1251 k_raw.len(),
1252 1,
1253 1,
1254 0,
1255 0,
1256 0,
1257 0,
1258 );
1259 k_p.proj_row_stride = kv_dim as u32;
1260 let mut v_p = Self::attention_gpu_params(
1261 &h,
1262 &layout,
1263 l,
1264 0,
1265 &v_info,
1266 v_raw.len(),
1267 2,
1268 1,
1269 0,
1270 0,
1271 0,
1272 0,
1273 );
1274 v_p.proj_row_stride = kv_dim as u32;
1275 let mut q_p = Self::attention_gpu_params(
1277 &h,
1278 &layout,
1279 l,
1280 0,
1281 &q_info,
1282 q_raw.len(),
1283 0,
1284 1,
1285 0,
1286 0,
1287 0,
1288 0,
1289 );
1290 q_p.proj_row_stride = q_dim as u32;
1291 layer_protos.push(LayerProtos {
1292 k_write: k_p,
1293 v_write: v_p,
1294 q: q_p,
1295 });
1296
1297 let dyn_base = l as u64 * ATTN_SLOTS_PER_LAYER;
1298 let want_triple = matches!(
1301 std::env::var("QUALIA_LLM_TRIPLE_QKV").ok().as_deref(),
1302 Some("1") | Some("true")
1303 );
1304 let triple_qkv = if want_triple
1305 && q_info.ggml_type == crate::ggml_quants::GGML_TYPE_Q4_K_SOA
1306 && k_info.ggml_type == crate::ggml_quants::GGML_TYPE_Q4_K_SOA
1307 && v_info.ggml_type == crate::ggml_quants::GGML_TYPE_Q4_K_SOA
1308 && use_coop
1309 {
1310 let tp = device.create_buffer(&wgpu::BufferDescriptor {
1312 label: Some("ResTripleQkvParams"),
1313 size: SLOT,
1314 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
1315 mapped_at_creation: false,
1316 });
1317 let mut p = gemm_params(
1318 q_info.ggml_type,
1319 n_embd,
1320 q_dim,
1321 q_info.dims[0] as u32,
1322 q_raw.len(),
1323 );
1324 p.weight_byte_len = kv_dim as u32;
1326 queue.write_buffer(&tp, 0, bytemuck::bytes_of(&p));
1327 Some(device.create_bind_group(&wgpu::BindGroupDescriptor {
1328 label: Some("ResTripleQkv"),
1329 layout: &self.triple_gemv_bind_layout,
1330 entries: &[
1331 wgpu::BindGroupEntry {
1332 binding: 0,
1333 resource: normed.as_entire_binding(),
1334 },
1335 wgpu::BindGroupEntry {
1336 binding: 1,
1337 resource: q_w.as_entire_binding(),
1338 },
1339 wgpu::BindGroupEntry {
1340 binding: 2,
1341 resource: tp.as_entire_binding(),
1342 },
1343 wgpu::BindGroupEntry {
1344 binding: 3,
1345 resource: q_proj.as_entire_binding(),
1346 },
1347 wgpu::BindGroupEntry {
1348 binding: 4,
1349 resource: k_w.as_entire_binding(),
1350 },
1351 wgpu::BindGroupEntry {
1352 binding: 5,
1353 resource: k_proj.as_entire_binding(),
1354 },
1355 wgpu::BindGroupEntry {
1356 binding: 6,
1357 resource: v_w.as_entire_binding(),
1358 },
1359 wgpu::BindGroupEntry {
1360 binding: 7,
1361 resource: v_proj.as_entire_binding(),
1362 },
1363 ],
1364 }))
1365 } else {
1366 None
1367 };
1368 let dual_kv = if triple_qkv.is_none()
1369 && k_info.ggml_type == crate::ggml_quants::GGML_TYPE_Q4_K_SOA
1370 && v_info.ggml_type == crate::ggml_quants::GGML_TYPE_Q4_K_SOA
1371 && use_coop
1372 {
1373 Some(device.create_bind_group(&wgpu::BindGroupDescriptor {
1374 label: Some("ResDualKv"),
1375 layout: &self.dual_gemv_bind_layout,
1376 entries: &[
1377 wgpu::BindGroupEntry {
1378 binding: 0,
1379 resource: normed.as_entire_binding(),
1380 },
1381 wgpu::BindGroupEntry {
1382 binding: 1,
1383 resource: k_w.as_entire_binding(),
1384 },
1385 wgpu::BindGroupEntry {
1386 binding: 2,
1387 resource: ubind(&static_arena, gbase * SLOT, gp_sz),
1388 },
1389 wgpu::BindGroupEntry {
1390 binding: 3,
1391 resource: k_proj.as_entire_binding(),
1392 },
1393 wgpu::BindGroupEntry {
1394 binding: 4,
1395 resource: v_w.as_entire_binding(),
1396 },
1397 wgpu::BindGroupEntry {
1398 binding: 5,
1399 resource: v_proj.as_entire_binding(),
1400 },
1401 ],
1402 }))
1403 } else {
1404 None
1405 };
1406 layers.push(LayerBinds {
1407 rms_attn: mk_elem_bg(
1408 "ResRmsAttn",
1409 &rms_layout,
1410 hidden_a.as_entire_binding(),
1411 norm_bind(2 * l as u64),
1412 &normed,
1413 ELEM_SLOT_RMS,
1414 ),
1415 triple_qkv,
1416 dual_kv,
1417 k_gemm: mk_gemm_bg("ResKGemm", &normed, k_w.as_entire_binding(), gbase, &k_proj),
1418 k_write: mk_attn_bg("ResKWrite", &k_proj, &k_w, dyn_base, l, &attn_out),
1419 v_gemm: mk_gemm_bg(
1420 "ResVGemm",
1421 &normed,
1422 v_w.as_entire_binding(),
1423 gbase + 1,
1424 &v_proj,
1425 ),
1426 v_write: mk_attn_bg("ResVWrite", &v_proj, &v_w, dyn_base + 1, l, &attn_out),
1427 q_gemm: mk_gemm_bg(
1428 "ResQGemm",
1429 &normed,
1430 q_w.as_entire_binding(),
1431 gbase + 2,
1432 &q_proj,
1433 ),
1434 q: mk_attn_bg("ResQSdpa", &q_proj, &q_w, dyn_base + 2, l, &attn_out),
1436 o_resid: mk_resid_bg(
1438 "ResOResid",
1439 &attn_out,
1440 o_w.as_entire_binding(),
1441 gbase + 3,
1442 &hidden_a,
1443 &hidden_b,
1444 ),
1445 rms_ffn: mk_elem_bg(
1446 "ResRmsFfn",
1447 &rms_layout,
1448 hidden_b.as_entire_binding(),
1449 norm_bind(2 * l as u64 + 1),
1450 &normed,
1451 ELEM_SLOT_RMS,
1452 ),
1453 fused_ffn: {
1456 let want = crate::llm_bench::ffn_fusion_enabled()
1457 && g_ty == u_ty
1458 && fused_ffn_quant_supported(g_ty);
1459 if want {
1460 Some(device.create_bind_group(&wgpu::BindGroupDescriptor {
1461 label: Some("ResFusedFfn"),
1462 layout: &self.ffn_fused_bind_layout,
1463 entries: &[
1464 wgpu::BindGroupEntry {
1465 binding: 0,
1466 resource: normed.as_entire_binding(),
1467 },
1468 wgpu::BindGroupEntry {
1469 binding: 1,
1470 resource: g_w.as_entire_binding(),
1471 },
1472 wgpu::BindGroupEntry {
1473 binding: 2,
1474 resource: u_w.as_entire_binding(),
1475 },
1476 wgpu::BindGroupEntry {
1477 binding: 3,
1478 resource: ubind(&static_arena, (gbase + 4) * SLOT, gp_sz),
1480 },
1481 wgpu::BindGroupEntry {
1482 binding: 4,
1483 resource: silu_buf.as_entire_binding(),
1484 },
1485 ],
1486 }))
1487 } else {
1488 None
1489 }
1490 },
1491 gate: {
1492 let want = !(crate::llm_bench::ffn_fusion_enabled()
1493 && g_ty == u_ty
1494 && fused_ffn_quant_supported(g_ty));
1495 if want {
1496 Some(mk_gemm_bg(
1497 "ResGate",
1498 &normed,
1499 g_w.as_entire_binding(),
1500 gbase + 4,
1501 &gate_buf,
1502 ))
1503 } else {
1504 None
1505 }
1506 },
1507 up: {
1508 let want = !(crate::llm_bench::ffn_fusion_enabled()
1509 && g_ty == u_ty
1510 && fused_ffn_quant_supported(g_ty));
1511 if want {
1512 Some(mk_gemm_bg(
1513 "ResUp",
1514 &normed,
1515 u_w.as_entire_binding(),
1516 gbase + 5,
1517 &up_buf,
1518 ))
1519 } else {
1520 None
1521 }
1522 },
1523 silu: {
1524 let want = !(crate::llm_bench::ffn_fusion_enabled()
1525 && g_ty == u_ty
1526 && fused_ffn_quant_supported(g_ty));
1527 if want {
1528 Some(mk_elem_bg(
1529 "ResSilu",
1530 &silu_layout,
1531 gate_buf.as_entire_binding(),
1532 up_buf.as_entire_binding(),
1533 &silu_buf,
1534 ELEM_SLOT_SILU,
1535 ))
1536 } else {
1537 None
1538 }
1539 },
1540 down_resid: mk_resid_bg(
1542 "ResDownResid",
1543 &silu_buf,
1544 d_w.as_entire_binding(),
1545 gbase + 6,
1546 &hidden_b,
1547 &hidden_a,
1548 ),
1549 });
1550 }
1551
1552 if !upload_norm(2 * n_layer as u64, &out_norm_info) {
1554 return None;
1555 }
1556 let rms_out = mk_elem_bg(
1557 "ResRmsOut",
1558 &rms_layout,
1559 hidden_a.as_entire_binding(),
1560 norm_bind(2 * n_layer as u64),
1561 &normed,
1562 ELEM_SLOT_RMS,
1563 );
1564
1565 let mut out_chunks = Vec::with_capacity(full_chunks);
1568 let mut cand_base_acc = 0u32;
1569 for c in 0..full_chunks {
1570 let row_start = c * RESIDENT_LOGITS_CHUNK;
1571 let rows = RESIDENT_LOGITS_CHUNK.min(vocab - row_start);
1572 if rows > RESIDENT_LOGITS_CHUNK {
1574 return None;
1575 }
1576 let cand_count = rows.div_ceil(block_size);
1577 let byte_len = rows as u64 * logits_row_bytes;
1578 let gemm_slot = n_layer as u64 * GEMM_SLOTS_PER_LAYER + c as u64;
1579 queue.write_buffer(
1580 &static_arena,
1581 gemm_slot * SLOT,
1582 bytemuck::bytes_of(&gemm_params(
1583 logits_info.ggml_type,
1584 n_embd,
1585 rows,
1586 logits_info.dims[0] as u32,
1587 byte_len as usize,
1588 )),
1589 );
1590 let tparams = crate::topk::topk_params_bytes_with_base(
1591 rows as u32,
1592 1,
1593 block_size as u32,
1594 cand_base_acc,
1595 );
1596 queue.write_buffer(&static_arena, topk_base + c as u64 * SLOT, &tparams);
1597 let weight = wgpu::BindingResource::Buffer(wgpu::BufferBinding {
1598 buffer: &logits_buf,
1599 offset: row_start as u64 * logits_row_bytes,
1600 size: std::num::NonZeroU64::new(byte_len),
1601 });
1602 let gemm = mk_gemm_bg("ResLogits", &normed, weight, gemm_slot, &logits_chunk);
1603 let topk = device.create_bind_group(&wgpu::BindGroupDescriptor {
1604 label: Some("ResTopk"),
1605 layout: &topk_layout,
1606 entries: &[
1607 wgpu::BindGroupEntry {
1608 binding: 0,
1609 resource: logits_chunk.as_entire_binding(),
1610 },
1611 wgpu::BindGroupEntry {
1612 binding: 1,
1613 resource: ubind(&static_arena, topk_base + c as u64 * SLOT, tp_sz),
1614 },
1615 wgpu::BindGroupEntry {
1616 binding: 2,
1617 resource: cand_val.as_entire_binding(),
1618 },
1619 wgpu::BindGroupEntry {
1620 binding: 3,
1621 resource: cand_idx.as_entire_binding(),
1622 },
1623 ],
1624 });
1625 out_chunks.push(OutChunk {
1626 gemm,
1627 topk,
1628 rows: rows as u32,
1629 cand_count,
1630 });
1631 cand_base_acc += cand_count as u32;
1632 }
1633
1634 let dyn_bytes = (n_layer as u64 * ATTN_SLOTS_PER_LAYER * SLOT) as usize;
1635 let use_fused_ffn = !layers.is_empty() && layers.iter().all(|lb| lb.fused_ffn.is_some());
1636 let use_triple = layers.iter().any(|lb| lb.triple_qkv.is_some());
1638 let use_dual = layers.iter().any(|lb| lb.dual_kv.is_some());
1639 let passes_per_layer = if use_fused_ffn {
1640 if use_triple {
1641 9
1642 } else if use_dual {
1643 10
1644 } else {
1645 11
1646 }
1647 } else {
1648 13
1649 };
1650 let passes_token = n_layer as usize * passes_per_layer + 1 + out_chunks.len() * 2;
1651 crate::llm_bench::set_ffn_fusion_in_resident(use_fused_ffn);
1652 let is_q4_soa = layer_gate_ggml_type == crate::ggml_quants::GGML_TYPE_Q4_K_SOA
1654 || logits_info.ggml_type == crate::ggml_quants::GGML_TYPE_Q4_K_SOA;
1655 let use_multirow = use_coop
1658 && is_q4_soa
1659 && matches!(
1660 std::env::var("QUALIA_LLM_MULTIROW").ok().as_deref(),
1661 Some("1") | Some("true")
1662 );
1663 let use_warp = use_coop
1664 && is_q4_soa
1665 && !use_multirow
1666 && matches!(
1667 std::env::var("QUALIA_LLM_WARP_GEMV").ok().as_deref(),
1668 Some("1") | Some("true")
1669 );
1670 let use_ffn_warp = use_coop
1672 && is_q4_soa
1673 && matches!(
1674 std::env::var("QUALIA_LLM_FFN_WARP").ok().as_deref(),
1675 Some("1") | Some("true")
1676 );
1677 let use_ffn_mr = use_coop
1678 && is_q4_soa
1679 && !use_ffn_warp
1680 && matches!(
1681 std::env::var("QUALIA_LLM_FFN_MR").ok().as_deref(),
1682 Some("1") | Some("true")
1683 );
1684 #[cfg(all(not(target_arch = "wasm32"), feature = "cuda"))]
1685 {
1686 let cuda_dense_cache = crate::weight_cache_len();
1687 let cuda_soa_weights = crate::q4k_device_weight_count();
1688 log::info!(
1689 "LLM_DECODE|resident-plan|built: {} layers, {} passes/token, fused_ffn={} triple_qkv={} dual_kv={} ffn_mr={} multirow={} cuda_soa_weights={} cuda_dense_cache={}",
1690 n_layer,
1691 passes_token,
1692 use_fused_ffn,
1693 use_triple,
1694 use_dual,
1695 use_ffn_mr,
1696 use_multirow,
1697 cuda_soa_weights,
1698 cuda_dense_cache,
1699 );
1700 }
1701 #[cfg(not(all(not(target_arch = "wasm32"), feature = "cuda")))]
1702 log::info!(
1703 "LLM_DECODE|resident-plan|built: {} layers, {} passes/token, fused_ffn={} ffn_mr={} ffn_warp={}",
1704 n_layer,
1705 passes_token,
1706 use_fused_ffn,
1707 use_ffn_mr,
1708 use_ffn_warp,
1709 );
1710 Some(Box::new(ResidentDecodePlan {
1711 key,
1712 n_embd,
1713 n_ffn,
1714 kv_dim,
1715 q_dim,
1716 n_head: n_head as u32,
1717 n_kv_head: n_kv as u32,
1718 layout,
1719 layer_protos,
1720 layers,
1721 rms_out,
1722 out_chunks,
1723 total_cands,
1724 attn_dyn_arena,
1725 dyn_scratch: vec![0u8; dyn_bytes],
1726 hidden_a,
1727 normed,
1728 staging,
1729 hidden_staging,
1730 use_coop,
1731 use_multirow,
1732 use_warp,
1733 use_ffn_mr,
1734 use_ffn_warp,
1735 use_fused_ffn,
1736 dispatches_per_token: passes_token as u32,
1737 readback_bytes_per_token: (total_cands * std::mem::size_of::<f32>()
1738 + total_cands * std::mem::size_of::<u32>())
1739 as u32,
1740 }))
1741 }
1742}