1use cortiq_core::CmfModel;
14use std::cell::Cell;
15use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicU64, Ordering};
16use std::sync::{Arc, OnceLock};
17
18thread_local! {
19 static CUR_LAYER: Cell<i64> = const { Cell::new(-1) };
23 static CPU_ONLY: Cell<bool> = const { Cell::new(false) };
28 static PROBE_COLD: Cell<bool> = const { Cell::new(false) };
32}
33
34pub fn cpu_scope<R>(f: impl FnOnce() -> R) -> R {
36 struct Restore(bool);
37 impl Drop for Restore {
38 fn drop(&mut self) {
39 CPU_ONLY.with(|c| c.set(self.0));
40 }
41 }
42 let previous = CPU_ONLY.with(|c| c.replace(true));
43 let _restore = Restore(previous);
44 f()
45}
46
47pub fn probe_set_device(label: &str) {
52 let _ = DEVICE_LABEL.set(label.to_string());
53}
54
55fn device_label() -> &'static str {
56 DEVICE_LABEL.get().map(String::as_str).unwrap_or("unknown")
57}
58
59static DEVICE_LABEL: std::sync::OnceLock<String> = std::sync::OnceLock::new();
60
61static CACHE_DIR: std::sync::OnceLock<std::path::PathBuf> = std::sync::OnceLock::new();
70
71pub fn set_cache_dir(dir: std::path::PathBuf) {
73 let _ = CACHE_DIR.set(dir);
74}
75
76pub fn cache_dir_pub() -> std::path::PathBuf {
78 cache_dir()
79}
80
81fn cache_dir() -> std::path::PathBuf {
82 if let Some(d) = CACHE_DIR.get() {
83 return d.clone();
84 }
85 match std::env::var_os("TMPDIR") {
86 Some(t) => std::path::PathBuf::from(t),
87 None => std::env::temp_dir(),
88 }
89}
90
91fn probe_cache_path() -> Option<std::path::PathBuf> {
94 match std::env::var("CMF_PROBE_CACHE") {
95 Ok(v) if v == "0" => None,
96 Ok(v) => Some(std::path::PathBuf::from(v)),
97 Err(_) => Some(cache_dir().join("cortiq-gpu-probe.tsv")),
98 }
99}
100
101fn probe_cache_key_named(class: &str) -> String {
105 format!(
106 "{}\t{}\t{}",
107 env!("CARGO_PKG_VERSION"),
108 device_label(),
109 class
110 )
111}
112
113const CLASS_NAMES: [&str; 7] = [
114 "ffn",
115 "matvec",
116 "matmat",
117 "qkv-batch",
118 "matmat-wide",
119 "lm-head",
120 "gemm-nt",
121];
122
123fn probe_cache_load() {
132 static ONCE: std::sync::Once = std::sync::Once::new();
133 ONCE.call_once(|| {
134 let Some(path) = probe_cache_path() else {
135 return;
136 };
137 if cfg!(test) && std::env::var("CMF_PROBE_CACHE").is_err() {
142 return;
143 }
144 let Ok(text) = std::fs::read_to_string(&path) else {
145 return;
146 };
147 probe_cache_adopt(&text);
148 });
149}
150
151fn probe_cache_adopt(text: &str) {
155 for line in text.lines() {
156 let Some((key, verdict)) = line.rsplit_once('\t') else {
157 continue;
158 };
159 let winner = match verdict.trim() {
160 "gpu" => 1u8,
161 "cpu" => 2u8,
162 _ => continue,
163 };
164 for (i, name) in CLASS_NAMES.iter().enumerate() {
165 if probe_cache_key_named(name) == key {
166 let _ = PROBES[i].state.compare_exchange(
167 0,
168 winner,
169 Ordering::Relaxed,
170 Ordering::Relaxed,
171 );
172 tracing::debug!("gpu probe [{name}]: remembered → {verdict}");
173 }
174 }
175 }
176}
177
178fn probe_cache_store(c: OpClass, winner: u8) {
181 let Some(path) = probe_cache_path() else {
182 return;
183 };
184 let line = format!(
185 "{}\t{}\n",
186 probe_cache_key_named(CLASS_NAMES[c as usize]),
187 if winner == 1 { "gpu" } else { "cpu" }
188 );
189 use std::io::Write;
190 if let Ok(mut f) = std::fs::OpenOptions::new()
191 .create(true)
192 .append(true)
193 .open(&path)
194 {
195 let _ = f.write_all(line.as_bytes());
196 }
197}
198
199pub fn cold_epoch() -> u64 {
205 COLD_EPOCH.load(std::sync::atomic::Ordering::Relaxed)
206}
207static COLD_EPOCH: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
208
209pub(crate) fn probe_note_cold() {
210 COLD_EPOCH.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
211 PROBE_COLD.with(|c| c.set(true));
212}
213
214pub(crate) fn probe_was_cold() -> bool {
218 PROBE_COLD.with(|c| c.get())
219}
220
221pub fn set_layer(l: i64) {
223 CUR_LAYER.with(|c| c.set(l));
224}
225
226pub fn cur_layer() -> i64 {
228 CUR_LAYER.with(|c| c.get())
229}
230
231fn layer_ranges() -> &'static Option<Vec<(i64, i64)>> {
234 static R: OnceLock<Option<Vec<(i64, i64)>>> = OnceLock::new();
235 R.get_or_init(|| {
236 let s = std::env::var("CMF_GPU_LAYERS").ok()?;
237 let mut v = Vec::new();
238 for part in s.split(',') {
239 let part = part.trim();
240 match part.split_once('-') {
241 Some((a, b)) => v.push((a.trim().parse().ok()?, b.trim().parse().ok()?)),
242 None => {
243 let x: i64 = part.parse().ok()?;
244 v.push((x, x));
245 }
246 }
247 }
248 Some(v)
249 })
250}
251
252fn layer_allowed() -> bool {
253 match layer_ranges() {
254 None => true,
255 Some(ranges) => {
256 let cur = CUR_LAYER.with(|c| c.get());
257 cur < 0 || ranges.iter().any(|(a, b)| cur >= *a && cur <= *b)
258 }
259 }
260}
261
262pub fn enabled_here() -> bool {
266 !CPU_ONLY.with(|c| c.get()) && enabled() && layer_allowed()
267}
268
269#[derive(Clone, Copy)]
281pub enum OpClass {
282 Ffn = 0,
284 Matvec = 1,
286 Matmat = 2,
288 Batch = 3,
290 MatmatWide = 4,
296 MatvecHead = 5,
303 GemmNt = 6,
310}
311
312pub fn matvec_class(rows: usize, cols: usize) -> OpClass {
316 if rows * cols >= 67_108_864 {
317 OpClass::MatvecHead
318 } else {
319 OpClass::Matvec
320 }
321}
322
323pub enum ProbeArm {
325 Gpu,
327 CpuTimed,
329 Cpu,
331}
332
333const PROBE_SAMPLES: u32 = 6;
335
336struct Probe {
337 state: AtomicU8,
339 flip: AtomicU32,
340 gpu_ns: AtomicU64,
341 gpu_n: AtomicU32,
342 cpu_ns: AtomicU64,
343 cpu_n: AtomicU32,
344 gpu_min: AtomicU64,
351 cpu_min: AtomicU64,
352}
353
354impl Probe {
355 const fn new() -> Self {
356 Self {
357 state: AtomicU8::new(0),
358 flip: AtomicU32::new(0),
359 gpu_ns: AtomicU64::new(0),
360 gpu_n: AtomicU32::new(0),
361 cpu_ns: AtomicU64::new(0),
362 cpu_n: AtomicU32::new(0),
363 gpu_min: AtomicU64::new(u64::MAX),
364 cpu_min: AtomicU64::new(u64::MAX),
365 }
366 }
367}
368
369static PROBES: [Probe; 7] = [
370 Probe::new(),
371 Probe::new(),
372 Probe::new(),
373 Probe::new(),
374 Probe::new(),
375 Probe::new(),
376 Probe::new(),
377];
378
379static TRUST_GPU: AtomicBool = AtomicBool::new(false);
386
387pub fn trust_gpu() -> GpuTrust {
389 let was = TRUST_GPU.swap(true, Ordering::Relaxed);
390 GpuTrust(was)
391}
392
393pub struct GpuTrust(bool);
394
395impl Drop for GpuTrust {
396 fn drop(&mut self) {
397 TRUST_GPU.store(self.0, Ordering::Relaxed);
398 }
399}
400
401fn probe_on() -> bool {
402 if TRUST_GPU.load(Ordering::Relaxed) {
403 return false;
404 }
405 static ON: OnceLock<bool> = OnceLock::new();
406 *ON.get_or_init(|| {
407 std::env::var("CMF_GPU_PROBE")
408 .map(|v| v != "0" && v != "off")
409 .unwrap_or(true)
410 })
411}
412
413pub fn q1_force() -> bool {
418 #[cfg(target_os = "macos")]
419 {
420 backend() == Backend::Metal
421 }
422 #[cfg(not(target_os = "macos"))]
423 {
424 false
425 }
426}
427
428pub fn fused_block_trusted() -> bool {
447 #[cfg(target_os = "macos")]
448 if backend() == Backend::Metal {
449 return true;
450 }
451 wgpu_graph_default()
452}
453
454pub fn weight_is_resident(model: &Arc<CmfModel>, idx: usize) -> bool {
466 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
467 {
468 return crate::gpu_wgpu::weight_is_resident(model, idx);
469 }
470 #[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
471 {
472 let _ = (model, idx);
473 true
474 }
475}
476
477pub fn probe_arm_cold_prefers_gpu(c: OpClass, weights_resident: bool) -> ProbeArm {
478 if !weights_resident && probe_deciding(c) {
479 return ProbeArm::Gpu;
480 }
481 probe_arm(c)
482}
483
484pub fn probe_arm(c: OpClass) -> ProbeArm {
485 PROBE_COLD.with(|f| f.set(false));
490 if !probe_on() {
491 return ProbeArm::Gpu;
492 }
493 probe_cache_load();
494 let p = &PROBES[c as usize];
495 match p.state.load(Ordering::Relaxed) {
496 1 => ProbeArm::Gpu,
497 2 => ProbeArm::Cpu,
498 _ => {
499 if p.flip.fetch_add(1, Ordering::Relaxed) % 2 == 0 {
500 ProbeArm::Gpu
501 } else {
502 ProbeArm::CpuTimed
503 }
504 }
505 }
506}
507
508pub fn probe_record(c: OpClass, gpu: bool, dur: std::time::Duration) {
511 let p = &PROBES[c as usize];
512 if p.state.load(Ordering::Relaxed) != 0 {
513 return;
514 }
515 if gpu && PROBE_COLD.with(|f| f.replace(false)) {
516 return; }
518 let ns = dur.as_nanos().min(u64::MAX as u128) as u64;
519 if gpu {
520 p.gpu_ns.fetch_add(ns, Ordering::Relaxed);
521 p.gpu_n.fetch_add(1, Ordering::Relaxed);
522 p.gpu_min.fetch_min(ns, Ordering::Relaxed);
523 } else {
524 p.cpu_ns.fetch_add(ns, Ordering::Relaxed);
525 p.cpu_n.fetch_add(1, Ordering::Relaxed);
526 p.cpu_min.fetch_min(ns, Ordering::Relaxed);
527 }
528 let (gn, cn) = (
529 p.gpu_n.load(Ordering::Relaxed),
530 p.cpu_n.load(Ordering::Relaxed),
531 );
532 if gn >= 2 && cn >= 2 {
533 let g = p.gpu_min.load(Ordering::Relaxed) as f64;
537 let cp = p.cpu_min.load(Ordering::Relaxed) as f64;
538 if (gn < PROBE_SAMPLES || cn < PROBE_SAMPLES) && g < cp * 2.0 && cp < g * 2.0 {
548 return;
549 }
550 let winner = if g <= cp { 1 } else { 2 };
551 if p.state
552 .compare_exchange(0, winner, Ordering::Relaxed, Ordering::Relaxed)
553 .is_ok()
554 {
555 tracing::info!(
556 "gpu probe [{}]: gpu {:.2} ms vs cpu {:.2} ms per op → {}",
557 CLASS_NAMES[c as usize],
558 g / 1e6,
559 cp / 1e6,
560 if winner == 1 { "gpu" } else { "cpu" },
561 );
562 probe_cache_store(c, winner);
563 }
564 }
565}
566
567pub fn probe_deciding(c: OpClass) -> bool {
570 probe_on() && PROBES[c as usize].state.load(Ordering::Relaxed) == 0
571}
572
573#[allow(unused_variables)]
583pub fn q8_resident_or_upload(model: &Arc<CmfModel>, idx: usize) -> bool {
584 static PROBE_UPLOADS: AtomicU32 = AtomicU32::new(0);
585 let may_upload = PROBE_UPLOADS.load(Ordering::Relaxed) < 4;
586 let resident = match backend() {
587 #[cfg(target_os = "macos")]
588 Backend::Metal => crate::gpu_metal::q8_resident_or_upload(model, idx, may_upload),
589 #[cfg(feature = "gpu")]
590 Backend::Wgpu => crate::gpu_wgpu::q8_resident_or_upload(model, idx, may_upload),
591 Backend::None => false,
592 };
593 if !resident && may_upload {
594 PROBE_UPLOADS.fetch_add(1, Ordering::Relaxed);
595 }
596 resident
597}
598
599#[cfg(test)]
601pub(crate) fn probe_reset() {
602 for p in &PROBES {
603 p.state.store(0, Ordering::Relaxed);
604 p.flip.store(0, Ordering::Relaxed);
605 p.gpu_ns.store(0, Ordering::Relaxed);
606 p.gpu_n.store(0, Ordering::Relaxed);
607 p.cpu_ns.store(0, Ordering::Relaxed);
608 p.cpu_n.store(0, Ordering::Relaxed);
609 }
610}
611
612#[cfg(test)]
613mod probe_tests {
614 use super::*;
615 use std::time::Duration;
616
617 #[test]
620 fn probe_alternates_discards_cold_and_decides() {
621 probe_reset();
622 assert!(matches!(probe_arm(OpClass::Ffn), ProbeArm::Gpu));
624 assert!(matches!(probe_arm(OpClass::Ffn), ProbeArm::CpuTimed));
625
626 probe_note_cold();
630 probe_record(OpClass::Ffn, true, Duration::from_secs(1000));
631 for _ in 0..PROBE_SAMPLES {
632 probe_record(OpClass::Ffn, true, Duration::from_millis(1));
633 probe_record(OpClass::Ffn, false, Duration::from_millis(4));
634 }
635 assert!(matches!(probe_arm(OpClass::Ffn), ProbeArm::Gpu));
636
637 for _ in 0..PROBE_SAMPLES {
639 probe_record(OpClass::Matmat, true, Duration::from_millis(4));
640 probe_record(OpClass::Matmat, false, Duration::from_millis(1));
641 }
642 assert!(matches!(probe_arm(OpClass::Matmat), ProbeArm::Cpu));
643
644 cpu_scope(|| CPU_ONLY.with(|c| assert!(c.get())));
646 CPU_ONLY.with(|c| assert!(!c.get()));
647 cpu_scope(|| {
648 cpu_scope(|| CPU_ONLY.with(|c| assert!(c.get())));
649 CPU_ONLY.with(|c| assert!(c.get()));
650 });
651 let _ = std::panic::catch_unwind(|| cpu_scope(|| panic!("scope test")));
652 CPU_ONLY.with(|c| assert!(!c.get()));
653 probe_reset();
654 }
655
656 #[test]
657 fn a_remembered_verdict_is_adopted_and_a_stranger_is_not() {
658 let mine = probe_cache_key_named("gemm-nt");
670 let state = || {
671 PROBES[OpClass::GemmNt as usize]
672 .state
673 .load(Ordering::Relaxed)
674 };
675
676 probe_cache_adopt("SomeOtherGPU/Vulkan\tgemm-nt\tgpu\n");
678 assert_eq!(state(), 0);
679 let older = mine.replacen(env!("CARGO_PKG_VERSION"), "0.0.0-old", 1);
681 assert_ne!(older, mine);
682 probe_cache_adopt(&format!("{older}\tgpu\n"));
683 assert_eq!(state(), 0);
684 probe_cache_adopt(&format!("{mine}\tcpu\n"));
686 assert_eq!(state(), 2);
687
688 PROBES[OpClass::GemmNt as usize]
689 .state
690 .store(0, Ordering::Relaxed);
691 }
692}
693
694pub const GPU_MIN_ROWS: usize = 65_536;
697
698pub fn min_rows() -> usize {
705 if let Some(v) = std::env::var("CMF_GPU_MIN_ROWS")
706 .ok()
707 .and_then(|v| v.parse().ok())
708 {
709 return v;
710 }
711 if discrete() { 4096 } else { GPU_MIN_ROWS }
712}
713
714pub fn discrete() -> bool {
716 match backend() {
717 #[cfg(feature = "gpu")]
718 Backend::Wgpu => crate::gpu_wgpu::is_discrete(),
719 #[cfg(target_os = "macos")]
720 Backend::Metal => false, Backend::None => false,
722 }
723}
724
725pub struct MoeJob<'a> {
729 pub gate: (usize, usize, usize, &'a [f32]),
730 pub up: (usize, usize, usize, &'a [f32]),
731 pub down: (usize, usize, usize, &'a [f32]),
732 pub xs_gate: Vec<f32>,
733 pub xs_up: Vec<f32>,
734 pub down_col: &'a [f32],
735 pub w: f32,
736 pub q1: bool,
739 pub q4t: bool,
742 pub q4tp: bool,
746 pub gu_q2: bool,
750 pub swiglu_limit: f32,
755}
756
757pub struct BatchJob<'a> {
759 pub idx: usize,
760 pub rows: usize,
761 pub cols: usize,
762 pub row_scale: &'a [f32],
763 pub xs: Vec<f32>,
764 pub layout: BatchLayout,
768}
769
770#[derive(Clone, Copy, PartialEq, Eq, Debug)]
773pub enum BatchLayout {
774 Q8,
775 Q1,
776 Q4t,
777 Q4tp,
778}
779
780#[derive(Clone, Copy, PartialEq, Eq)]
781enum Backend {
782 None,
783 #[cfg(target_os = "macos")]
784 Metal,
785 #[cfg(feature = "gpu")]
786 Wgpu,
787}
788
789fn backend() -> Backend {
790 #[cfg(feature = "gpu")]
791 if crate::gpu_wgpu::selected() {
792 return if crate::gpu_wgpu::enabled() {
793 Backend::Wgpu
794 } else {
795 Backend::None
796 };
797 }
798 #[cfg(target_os = "macos")]
799 if crate::gpu_metal::enabled() {
800 return Backend::Metal;
801 }
802 Backend::None
803}
804
805pub fn backend_available() -> bool {
811 #[cfg(target_os = "macos")]
812 {
813 true
815 }
816 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
817 {
818 static AVAIL: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
819 *AVAIL.get_or_init(crate::gpu_wgpu::adapter_probe)
820 }
821 #[cfg(all(not(feature = "gpu"), not(target_os = "macos")))]
822 {
823 false
824 }
825}
826
827static GPU_PAUSED: AtomicBool = AtomicBool::new(false);
833
834pub fn pause_gpu() -> GpuPause {
836 GPU_PAUSED.store(true, Ordering::Relaxed);
837 GpuPause(())
838}
839
840pub struct GpuPause(());
841
842impl Drop for GpuPause {
843 fn drop(&mut self) {
844 GPU_PAUSED.store(false, Ordering::Relaxed);
845 }
846}
847
848pub fn enabled() -> bool {
849 !GPU_PAUSED.load(Ordering::Relaxed) && backend() != Backend::None
850}
851
852pub fn wgpu_active() -> bool {
866 #[cfg(feature = "gpu")]
867 {
868 matches!(backend(), Backend::Wgpu)
869 }
870 #[cfg(not(feature = "gpu"))]
871 {
872 false
873 }
874}
875
876pub fn default_device() -> usize {
883 static D: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
884 *D.get_or_init(|| {
885 std::env::var("CMF_GPU_ADAPTER")
886 .ok()
887 .and_then(|v| v.trim().parse::<usize>().ok())
888 .unwrap_or(0)
889 })
890}
891
892thread_local! {
893 static CUR_DEV: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
894}
895
896pub fn current_device() -> usize {
898 CUR_DEV.with(|c| c.get()).unwrap_or_else(default_device)
899}
900
901pub fn set_current_device(i: usize) {
905 CUR_DEV.with(|c| c.set(Some(i)));
906}
907
908pub fn with_device<R>(dev: usize, f: impl FnOnce() -> R) -> R {
910 let prev = CUR_DEV.with(|c| c.replace(Some(dev)));
911 let r = f();
912 CUR_DEV.with(|c| c.set(prev));
913 r
914}
915
916pub fn device_count() -> usize {
919 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
920 {
921 return crate::gpu_wgpu::adapter_count();
922 }
923 #[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
924 {
925 usize::from(backend_available())
926 }
927}
928
929pub fn vram_budget() -> u64 {
933 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
934 {
935 return crate::gpu_wgpu::device_vram_budget();
936 }
937 #[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
938 {
939 if backend_available() { u64::MAX } else { 0 }
940 }
941}
942
943pub fn upload_bytes() -> u64 {
947 #[cfg(feature = "gpu")]
948 {
949 return crate::gpu_wgpu::UPLOAD_BYTES.load(std::sync::atomic::Ordering::Relaxed);
950 }
951 #[cfg(not(feature = "gpu"))]
952 0
953}
954
955#[derive(Clone, Copy, PartialEq, Eq, Debug)]
967pub enum GraphPhase {
968 Prefill,
969 Decode,
970}
971
972pub fn wgpu_graph_on(phase: GraphPhase) -> bool {
980 match std::env::var("CMF_GPU_WGPU_GRAPH").ok().as_deref() {
981 Some("0") => false,
982 Some("prefill") => phase == GraphPhase::Prefill,
983 Some(_) => true,
984 None => {
985 if wgpu_graph_default() {
986 return true;
987 }
988 let _ = phase;
993 false
994 }
995 }
996}
997
998pub fn wgpu_graph_default() -> bool {
999 #[cfg(feature = "gpu")]
1000 {
1001 matches!(backend(), Backend::Wgpu)
1007 && (crate::gpu_wgpu::discrete_active()
1008 || (cfg!(target_os = "macos") && crate::gpu_wgpu::adapter_up()))
1009 }
1010 #[cfg(not(feature = "gpu"))]
1011 {
1012 false
1013 }
1014}
1015
1016#[allow(clippy::too_many_arguments, unused_variables)]
1018pub fn q8_matvec_range(
1019 model: &Arc<CmfModel>,
1020 idx: usize,
1021 row0: usize,
1022 row_scale: &[f32],
1023 xs: &[f32],
1024 rows: usize,
1025 cols: usize,
1026 out: &mut [f32],
1027) -> bool {
1028 match backend() {
1029 #[cfg(target_os = "macos")]
1030 Backend::Metal => {
1031 crate::gpu_metal::q8_matvec_range(model, idx, row0, row_scale, xs, rows, cols, out)
1032 }
1033 #[cfg(feature = "gpu")]
1034 Backend::Wgpu => {
1035 crate::gpu_wgpu::q8_matvec_range(model, idx, row0, row_scale, xs, rows, cols, out)
1036 }
1037 Backend::None => false,
1038 }
1039}
1040
1041#[allow(clippy::too_many_arguments, unused_variables)]
1044pub fn q8_matmat(
1045 model: &Arc<CmfModel>,
1046 idx: usize,
1047 row_scale: &[f32],
1048 pre: &[f32],
1049 b: usize,
1050 rows: usize,
1051 cols: usize,
1052 out: &mut [f32],
1053) -> bool {
1054 match backend() {
1055 #[cfg(target_os = "macos")]
1056 Backend::Metal => {
1057 crate::gpu_metal::q8_matmat(model, idx, row_scale, pre, b, rows, cols, out)
1058 }
1059 #[cfg(feature = "gpu")]
1060 Backend::Wgpu => crate::gpu_wgpu::q8_matmat(model, idx, row_scale, pre, b, rows, cols, out),
1061 Backend::None => false,
1062 }
1063}
1064
1065#[allow(unused_variables)]
1068pub fn q1_matvec(
1069 model: &Arc<CmfModel>,
1070 idx: usize,
1071 xs: &[f32],
1072 rows: usize,
1073 cols: usize,
1074 out: &mut [f32],
1075) -> bool {
1076 match backend() {
1077 #[cfg(target_os = "macos")]
1078 Backend::Metal => crate::gpu_metal::q1_matvec(model, idx, xs, rows, cols, out),
1079 #[cfg(feature = "gpu")]
1080 Backend::Wgpu => crate::gpu_wgpu::q1_matvec(model, idx, xs, rows, cols, out),
1081 Backend::None => false,
1082 }
1083}
1084
1085#[allow(clippy::too_many_arguments)]
1089pub fn attn_dropin(
1090 model: &Arc<CmfModel>,
1091 kv_id: u64,
1092 layer: usize,
1093 normed: &[f32],
1094 wq_idx: usize,
1095 wk_idx: usize,
1096 wv_idx: usize,
1097 wo_idx: usize,
1098 q_norm: Option<&[f32]>,
1099 k_norm: Option<&[f32]>,
1100 invf: &[f32],
1101 nh: usize,
1102 nkv: usize,
1103 hd: usize,
1104 rd: usize,
1105 hidden: usize,
1106 pos: usize,
1107 cap: usize,
1108 gemma: bool,
1109 eps: f32,
1110 cpu_k: &[Vec<f32>],
1111 cpu_v: &[Vec<f32>],
1112 out: &mut [f32],
1113) -> bool {
1114 match backend() {
1115 #[cfg(feature = "gpu")]
1116 Backend::Wgpu => crate::gpu_wgpu::attn_dropin_gpu(
1117 model, kv_id, layer, normed, wq_idx, wk_idx, wv_idx, wo_idx, q_norm, k_norm, invf, nh,
1118 nkv, hd, rd, hidden, pos, cap, gemma, eps, cpu_k, cpu_v, out,
1119 ),
1120 #[allow(unused_variables)]
1121 _ => false,
1122 }
1123}
1124
1125pub struct GraphW<'a> {
1129 pub idx: usize,
1130 pub kind: u8,
1131 pub row_scale: &'a [f32],
1132 pub data: &'a [f32],
1133}
1134
1135pub enum GraphAttn<'a> {
1138 Full {
1139 wq: GraphW<'a>,
1140 wk: GraphW<'a>,
1141 wv: GraphW<'a>,
1142 wo: GraphW<'a>,
1143 q_norm: Option<&'a [f32]>,
1144 k_norm: Option<&'a [f32]>,
1145 bias: Option<(&'a [f32], &'a [f32], &'a [f32])>,
1147 output_gate: bool,
1150 cpu_k: &'a [Vec<f32>],
1151 cpu_v: &'a [Vec<f32>],
1152 },
1153 Gdn {
1154 qkv: GraphW<'a>,
1155 z: GraphW<'a>,
1156 a: GraphW<'a>,
1157 b: GraphW<'a>,
1158 out: GraphW<'a>,
1159 conv1d: &'a [f32],
1160 a_log: &'a [f32],
1161 dt_bias: &'a [f32],
1162 norm: &'a [f32],
1163 nv: usize,
1164 nk: usize,
1165 dk: usize,
1166 dv: usize,
1167 kk: usize,
1168 cpu_state: &'a [f32],
1173 },
1174}
1175
1176pub struct GraphLayer<'a> {
1178 pub input_norm: &'a [f32],
1179 pub attn: GraphAttn<'a>,
1180 pub post_norm: &'a [f32],
1181 pub ffn: GraphFfn<'a>,
1182}
1183
1184pub enum GraphFfn<'a> {
1189 Dense {
1190 gate: GraphW<'a>,
1191 up: GraphW<'a>,
1192 down: GraphW<'a>,
1193 },
1194 Moe {
1195 router: GraphW<'a>,
1197 shared_gate: GraphW<'a>,
1199 experts: Vec<(usize, usize, usize)>,
1203 n_exp: usize,
1205 top_k: usize,
1206 inter: usize,
1207 norm_topk: bool,
1208 q4tp: bool,
1214 gu_q2: bool,
1218 },
1219}
1220
1221#[allow(clippy::too_many_arguments)]
1226pub fn forward_token_graph(
1227 model: &Arc<CmfModel>,
1228 kv_id: u64,
1229 layers: &[GraphLayer],
1230 o1: &[Option<Vec<crate::nystrom::O1DeviceView<'_>>>],
1233 o1_epoch: u64,
1234 invf: &[f32],
1235 h: &mut [f32],
1236 nh: usize,
1237 nkv: usize,
1238 hd: usize,
1239 rd: usize,
1240 hidden: usize,
1241 inter: usize,
1242 position: usize,
1243 cap: usize,
1244 gemma: bool,
1245 eps: f32,
1246 lm_head: Option<(&GraphW, usize)>,
1247 final_norm: &[f32],
1248 logits: &mut Vec<f32>,
1249 loop_norm_at: &[usize],
1250 steps: usize,
1251 embed: Option<(&GraphW, usize, f32)>,
1252 ids_out: Option<&mut Vec<u32>>,
1253 layers_run: Option<&mut usize>,
1256 layer_base: usize,
1260 hidden_too: bool,
1262) -> bool {
1263 match backend() {
1264 #[cfg(feature = "gpu")]
1265 Backend::Wgpu => crate::gpu_wgpu::forward_token_graph(
1266 model,
1267 kv_id,
1268 layers,
1269 o1,
1270 o1_epoch,
1271 invf,
1272 h,
1273 nh,
1274 nkv,
1275 hd,
1276 rd,
1277 hidden,
1278 inter,
1279 position,
1280 cap,
1281 gemma,
1282 eps,
1283 lm_head,
1284 final_norm,
1285 logits,
1286 loop_norm_at,
1287 steps,
1288 embed,
1289 ids_out,
1290 layers_run,
1291 layer_base,
1292 hidden_too,
1293 ),
1294 #[allow(unused_variables)]
1295 _ => {
1296 let _ = (
1297 lm_head,
1298 final_norm,
1299 logits,
1300 loop_norm_at,
1301 layers_run,
1302 layer_base,
1303 hidden_too,
1304 );
1305 false
1306 }
1307 }
1308}
1309
1310pub struct SpecTail<'a> {
1314 pub lm: GraphW<'a>,
1315 pub lm_rows: usize,
1316 pub final_norm: &'a [f32],
1317 pub logits_out: &'a mut Vec<f32>,
1318}
1319
1320#[allow(clippy::too_many_arguments)]
1324pub fn forward_batch_graph(
1325 model: &Arc<CmfModel>,
1326 kv_id: u64,
1327 layers: &[GraphLayer],
1328 invf: &[f32],
1329 h: &mut [f32],
1330 nh: usize,
1331 nkv: usize,
1332 hd: usize,
1333 rd: usize,
1334 hidden: usize,
1335 inter: usize,
1336 positions: &[usize],
1337 cap: usize,
1338 gemma: bool,
1339 eps: f32,
1340 k: usize,
1341 spec: Option<SpecTail<'_>>,
1342) -> bool {
1343 match backend() {
1344 #[cfg(feature = "gpu")]
1345 Backend::Wgpu => crate::gpu_wgpu::forward_batch_graph(
1346 model, kv_id, layers, invf, h, nh, nkv, hd, rd, hidden, inter, positions, cap, gemma,
1347 eps, k, spec,
1348 ),
1349 #[allow(unreachable_patterns)]
1350 _ => {
1351 let _ = spec;
1352 false
1353 }
1354 }
1355}
1356
1357pub fn gdn_spec_restore(kv_id: u64, slot: usize) -> bool {
1360 #[cfg(feature = "gpu")]
1361 if backend() == Backend::Wgpu {
1362 return crate::gpu_wgpu::gdn_spec_restore(kv_id, slot);
1363 }
1364 #[allow(unreachable_code)]
1365 {
1366 let _ = (kv_id, slot);
1367 false
1368 }
1369}
1370
1371pub fn graph_kv_reset(_kv_id: u64) {
1373 #[cfg(feature = "gpu")]
1374 if backend() == Backend::Wgpu {
1375 crate::gpu_wgpu::kv_mirror_reset(_kv_id);
1376 }
1377}
1378
1379pub fn q1t_matvec(
1383 model: &Arc<CmfModel>,
1384 idx: usize,
1385 xs: &[f32],
1386 rows: usize,
1387 cols: usize,
1388 out: &mut [f32],
1389) -> bool {
1390 match backend() {
1391 #[cfg(target_os = "macos")]
1392 Backend::Metal => {
1393 if metal_q1t_enabled() {
1394 crate::gpu_metal::q1t_matvec(model, idx, xs, rows, cols, out)
1395 } else {
1396 false
1397 }
1398 }
1399 #[cfg(feature = "gpu")]
1400 Backend::Wgpu => crate::gpu_wgpu::q1t_matvec(model, idx, xs, rows, cols, out),
1401 Backend::None => false,
1402 }
1403}
1404
1405#[allow(unused_variables)]
1408pub fn q4b_matvec(
1409 model: &Arc<CmfModel>,
1410 idx: usize,
1411 xs: &[f32],
1412 rows: usize,
1413 cols: usize,
1414 out: &mut [f32],
1415) -> bool {
1416 match backend() {
1417 #[cfg(target_os = "macos")]
1418 Backend::Metal => false,
1419 #[cfg(feature = "gpu")]
1420 Backend::Wgpu => crate::gpu_wgpu::q4b_matvec(model, idx, xs, rows, cols, out),
1421 Backend::None => false,
1422 }
1423}
1424
1425pub fn q1t_matmat(
1428 model: &Arc<CmfModel>,
1429 idx: usize,
1430 xs: &[f32],
1431 b: usize,
1432 rows: usize,
1433 cols: usize,
1434 out: &mut [f32],
1435) -> bool {
1436 match backend() {
1437 #[cfg(target_os = "macos")]
1438 Backend::Metal => crate::gpu_metal::q1t_matmat(model, idx, xs, b, rows, cols, out),
1442 #[cfg(feature = "gpu")]
1443 Backend::Wgpu => crate::gpu_wgpu::q1t_matmat(model, idx, xs, b, rows, cols, out),
1444 Backend::None => false,
1445 }
1446}
1447
1448#[cfg(target_os = "macos")]
1452pub(crate) fn metal_q1t_enabled() -> bool {
1453 std::env::var("CMF_METAL_Q1T")
1454 .map(|v| v != "0" && !v.eq_ignore_ascii_case("off"))
1455 .unwrap_or(true)
1456}
1457
1458pub fn q1_matmat(
1460 model: &Arc<CmfModel>,
1461 idx: usize,
1462 xs: &[f32],
1463 b: usize,
1464 rows: usize,
1465 cols: usize,
1466 out: &mut [f32],
1467) -> bool {
1468 match backend() {
1469 #[cfg(feature = "gpu")]
1470 Backend::Wgpu => crate::gpu_wgpu::q1_matmat(model, idx, xs, b, rows, cols, out),
1471 #[allow(unused_variables)]
1472 _ => false,
1473 }
1474}
1475
1476static MM_KILL: AtomicBool = AtomicBool::new(false);
1481pub(crate) fn mm_killed() -> bool {
1482 MM_KILL.load(Ordering::Relaxed)
1483}
1484pub(crate) fn mm_kill() {
1485 MM_KILL.store(true, Ordering::Relaxed);
1486}
1487
1488static MM_STRIKES: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
1495const MM_STRIKES_TO_KILL: u32 = 3;
1496static MM_ARMED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true);
1503
1504pub fn mm_kill_arm(on: bool) {
1507 MM_ARMED.store(on, Ordering::Relaxed);
1508 if on {
1509 MM_STRIKES.store(0, Ordering::Relaxed);
1510 }
1511}
1512
1513pub(crate) fn mm_budget_check(
1520 what: &str,
1521 el: std::time::Duration,
1522 budget: std::time::Duration,
1523 exempt: bool,
1524) {
1525 if el <= budget {
1526 MM_STRIKES.store(0, Ordering::Relaxed);
1527 return;
1528 }
1529 if exempt || !MM_ARMED.load(Ordering::Relaxed) {
1530 return;
1531 }
1532 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1533 let on = *ON.get_or_init(|| std::env::var("CMF_MM_KILL").as_deref() != Ok("0"));
1534 let n = MM_STRIKES.fetch_add(1, Ordering::Relaxed) + 1;
1535 if !on {
1536 tracing::info!(
1537 "gpu {what} took {el:?} (budget {budget:?}) — over budget, CMF_MM_KILL=0 keeps the device"
1538 );
1539 return;
1540 }
1541 if n >= MM_STRIKES_TO_KILL {
1542 tracing::warn!(
1543 "gpu {what} took {el:?} (budget {budget:?}), {n} in a row — \
1544 device contended, CPU for the rest of the process (CMF_MM_KILL=0 to override)"
1545 );
1546 mm_kill();
1547 } else {
1548 tracing::info!(
1549 "gpu {what} took {el:?} (budget {budget:?}) — strike {n} of {MM_STRIKES_TO_KILL}"
1550 );
1551 }
1552}
1553
1554#[allow(unused_variables, clippy::too_many_arguments)]
1559pub fn chunk_attend(
1560 q: &[f32],
1561 k: &[&[f32]],
1562 v: &[&[f32]],
1563 b: usize,
1564 s0: usize,
1565 nh: usize,
1566 nkv: usize,
1567 hd: usize,
1568 scale: f32,
1569 out: &mut [f32],
1570) -> bool {
1571 match backend() {
1572 #[cfg(feature = "gpu")]
1573 Backend::Wgpu => crate::gpu_wgpu::chunk_attend(q, k, v, b, s0, nh, nkv, hd, scale, out),
1574 #[allow(unreachable_patterns)]
1575 _ => false,
1576 }
1577}
1578
1579#[allow(unused_variables, clippy::too_many_arguments)]
1583pub fn q4t_qkv(
1584 model: &Arc<CmfModel>,
1585 wq: usize,
1586 wk: usize,
1587 wv: usize,
1588 xs: &[f32],
1589 b: usize,
1590 cols: usize,
1591 rq: usize,
1592 rk: usize,
1593 rv: usize,
1594 out: &mut [f32],
1595) -> bool {
1596 match backend() {
1597 #[cfg(feature = "gpu")]
1598 Backend::Wgpu => crate::gpu_wgpu::q4t_qkv(model, wq, wk, wv, xs, b, cols, rq, rk, rv, out),
1599 #[allow(unreachable_patterns)]
1600 _ => false,
1601 }
1602}
1603
1604#[allow(unused_variables, clippy::too_many_arguments)]
1606#[allow(clippy::too_many_arguments, unused_variables)]
1610pub fn q4tp_ffn_packed(
1611 model: &Arc<CmfModel>,
1612 w1: usize,
1613 w2: usize,
1614 xs: &[f32],
1615 b: usize,
1616 hidden: usize,
1617 inter: usize,
1618 bias: Option<&[f32]>,
1619 out: &mut [f32],
1620) -> bool {
1621 match backend() {
1622 #[cfg(feature = "gpu")]
1623 Backend::Wgpu => {
1624 crate::gpu_wgpu::q4tp_ffn_packed(model, w1, w2, xs, b, hidden, inter, bias, out)
1625 }
1626 #[allow(unreachable_patterns)]
1627 _ => false,
1628 }
1629}
1630
1631pub fn q4tp_ffn(
1632 model: &Arc<CmfModel>,
1633 w1: usize,
1634 w3: usize,
1635 w2: usize,
1636 xs: &[f32],
1637 b: usize,
1638 hidden: usize,
1639 inter: usize,
1640 out: &mut [f32],
1641) -> bool {
1642 match backend() {
1643 #[cfg(target_os = "macos")]
1644 Backend::Metal => crate::gpu_metal::q4tp_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
1645 #[cfg(feature = "gpu")]
1646 Backend::Wgpu => crate::gpu_wgpu::q4tp_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
1647 #[allow(unreachable_patterns)]
1648 _ => false,
1649 }
1650}
1651
1652pub fn q4t_ffn(
1653 model: &Arc<CmfModel>,
1654 w1: usize,
1655 w3: usize,
1656 w2: usize,
1657 xs: &[f32],
1658 b: usize,
1659 hidden: usize,
1660 inter: usize,
1661 out: &mut [f32],
1662) -> bool {
1663 match backend() {
1664 #[cfg(target_os = "macos")]
1665 Backend::Metal => crate::gpu_metal::q4t_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
1666 #[cfg(feature = "gpu")]
1667 Backend::Wgpu => crate::gpu_wgpu::q4t_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
1668 #[allow(unreachable_patterns)]
1669 _ => false,
1670 }
1671}
1672
1673pub struct DitBlockArgs<'a> {
1678 pub n: usize,
1679 pub hidden: usize,
1680 pub inter: usize,
1681 pub nh: usize,
1682 pub nkv: usize,
1683 pub hd: usize,
1684 pub eps: f32,
1685 pub rope_cos: &'a [f32],
1686 pub rope_sin: &'a [f32],
1687 pub norm1: &'a [f32],
1688 pub norm2: &'a [f32],
1689 pub ffn_norm1: &'a [f32],
1690 pub ffn_norm2: &'a [f32],
1691 pub norm_q: &'a [f32],
1692 pub norm_k: &'a [f32],
1693 pub s_msa: &'a [f32],
1694 pub gate_msa: &'a [f32],
1695 pub s_mlp: &'a [f32],
1696 pub gate_mlp: &'a [f32],
1697 pub wq: usize,
1698 pub wk: usize,
1699 pub wv: usize,
1700 pub wo: usize,
1701 pub w1: usize,
1702 pub w3: usize,
1703 pub w2: usize,
1704 pub q4tp: bool,
1708 pub resident_in: bool,
1711 pub resident_out: bool,
1715}
1716
1717pub fn dit_chain_supported() -> bool {
1721 #[cfg(feature = "gpu")]
1722 {
1723 return matches!(backend(), Backend::Wgpu) && fused_dit_block_available();
1724 }
1725 #[allow(unreachable_code)]
1726 false
1727}
1728
1729pub fn dit_state_fetch(_x: &mut [f32]) -> bool {
1732 #[cfg(feature = "gpu")]
1733 {
1734 if matches!(backend(), Backend::Wgpu) {
1735 return crate::gpu_wgpu::dit_state_fetch(_x);
1736 }
1737 }
1738 false
1739}
1740
1741#[allow(unused_variables)]
1745#[allow(unused_variables, clippy::too_many_arguments)]
1749pub fn dit_qkv(
1750 model: &Arc<CmfModel>,
1751 wq: usize,
1752 wk: usize,
1753 wv: usize,
1754 xs: &[f32],
1755 b: usize,
1756 hidden: usize,
1757 qrows: usize,
1758 kvrows: usize,
1759 q_out: &mut [f32],
1760 k_out: &mut [f32],
1761 v_out: &mut [f32],
1762) -> bool {
1763 match backend() {
1764 #[cfg(feature = "gpu")]
1765 Backend::Wgpu => crate::gpu_wgpu::q4tp_qkv(
1766 model, wq, wk, wv, xs, b, hidden, qrows, kvrows, q_out, k_out, v_out,
1767 ),
1768 #[allow(unreachable_patterns)]
1769 _ => false,
1770 }
1771}
1772
1773pub fn fused_dit_block_available() -> bool {
1777 #[cfg(target_os = "macos")]
1778 {
1779 matches!(backend(), Backend::Metal) && fused_block_trusted()
1780 }
1781 #[cfg(not(target_os = "macos"))]
1782 {
1783 false
1784 }
1785}
1786
1787pub fn dit_block(model: &Arc<CmfModel>, a: &DitBlockArgs, x: &mut [f32]) -> bool {
1788 dit_block_seg(model, a, &[a.n], x)
1789}
1790
1791pub fn dit_block_seg(
1795 model: &Arc<CmfModel>,
1796 a: &DitBlockArgs,
1797 segs: &[usize],
1798 x: &mut [f32],
1799) -> bool {
1800 match backend() {
1801 #[cfg(target_os = "macos")]
1802 Backend::Metal if segs.len() <= 1 => crate::gpu_metal::dit_block(model, a, x),
1803 #[cfg(feature = "gpu")]
1810 Backend::Wgpu
1811 if match std::env::var("CMF_DIT_FUSED").ok().as_deref() {
1812 Some("0") => false,
1813 Some(_) => true,
1814 None => crate::gpu_wgpu::discrete_active(),
1815 } =>
1816 {
1817 crate::gpu_wgpu::dit_block_seg(model, a, segs, x)
1818 }
1819 #[allow(unreachable_patterns)]
1820 _ => false,
1821 }
1822}
1823
1824pub struct VaeResnetArgs<'a> {
1828 pub groups: usize,
1829 pub ic: usize,
1830 pub oc: usize,
1831 pub h: usize,
1832 pub w: usize,
1833 pub n1w: &'a [f32],
1834 pub n1b: &'a [f32],
1835 pub c1w: &'a [f32],
1836 pub c1b: &'a [f32],
1837 pub c1k: usize,
1838 pub n2w: &'a [f32],
1839 pub n2b: &'a [f32],
1840 pub c2w: &'a [f32],
1841 pub c2b: &'a [f32],
1842 pub c2k: usize,
1843 pub shortcut: Option<(&'a [f32], &'a [f32], usize)>,
1844}
1845
1846#[allow(unused_variables)]
1849pub fn vae_resnet(a: &VaeResnetArgs, x: &[f32], out: &mut [f32]) -> bool {
1850 match backend() {
1851 #[cfg(target_os = "macos")]
1852 Backend::Metal => crate::gpu_metal::vae_resnet(a, x, out),
1853 _ => false,
1854 }
1855}
1856
1857#[allow(unused_variables, clippy::too_many_arguments)]
1860pub fn vae_upsample_conv(
1861 w: &[f32],
1862 bias: &[f32],
1863 x: &[f32],
1864 ic: usize,
1865 oc: usize,
1866 h: usize,
1867 w_img: usize,
1868 k: usize,
1869 out: &mut [f32],
1870) -> bool {
1871 match backend() {
1872 #[cfg(target_os = "macos")]
1873 Backend::Metal => crate::gpu_metal::vae_upsample_conv(w, bias, x, ic, oc, h, w_img, k, out),
1874 #[cfg(feature = "gpu")]
1875 Backend::Wgpu => crate::gpu_wgpu::vae_upsample_conv(w, bias, x, ic, oc, h, w_img, k, out),
1876 #[allow(unreachable_patterns)]
1877 _ => false,
1878 }
1879}
1880
1881#[allow(unused_variables, clippy::too_many_arguments)]
1884pub fn vae_conv2d(
1885 w: &[f32],
1886 bias: &[f32],
1887 x: &[f32],
1888 ic: usize,
1889 oc: usize,
1890 h: usize,
1891 w_img: usize,
1892 k: usize,
1893 out: &mut [f32],
1894) -> bool {
1895 match backend() {
1896 #[cfg(target_os = "macos")]
1897 Backend::Metal => crate::gpu_metal::vae_conv2d(w, bias, x, ic, oc, h, w_img, k, out),
1898 #[cfg(feature = "gpu")]
1899 Backend::Wgpu => crate::gpu_wgpu::vae_conv2d(w, bias, x, ic, oc, h, w_img, k, out),
1900 #[allow(unreachable_patterns)]
1901 _ => false,
1902 }
1903}
1904
1905#[allow(unused_variables, clippy::too_many_arguments)]
1909#[allow(unused_variables)]
1913#[allow(clippy::too_many_arguments)]
1914#[allow(clippy::too_many_arguments, unused_variables)]
1917pub fn dit_qkv_attention(
1918 model: &Arc<CmfModel>,
1919 qkv_idx: usize,
1920 xn: &[f32],
1921 n: usize,
1922 hidden: usize,
1923 nh: usize,
1924 hd: usize,
1925 scale: f32,
1926 nr: (&[f32], &[f32], &[f32], f32),
1927 out: &mut [f32],
1928) -> bool {
1929 match backend() {
1930 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
1931 Backend::Wgpu => crate::gpu_wgpu::dit_qkv_attention(
1932 model, qkv_idx, xn, n, hidden, nh, hd, scale, nr, out,
1933 ),
1934 #[allow(unreachable_patterns)]
1935 _ => false,
1936 }
1937}
1938
1939#[allow(clippy::too_many_arguments)]
1942pub fn dit_qkv_attn_out(
1943 model: &Arc<CmfModel>,
1944 qkv_idx: usize,
1945 out_idx: usize,
1946 xn: &[f32],
1947 n: usize,
1948 hidden: usize,
1949 nh: usize,
1950 hd: usize,
1951 scale: f32,
1952 nr: (&[f32], &[f32], &[f32], f32),
1953 proj: &mut [f32],
1954) -> bool {
1955 match backend() {
1956 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
1957 Backend::Wgpu => crate::gpu_wgpu::dit_qkv_attn_out(
1958 model, qkv_idx, out_idx, xn, n, hidden, nh, hd, scale, nr, proj,
1959 ),
1960 #[allow(unreachable_patterns)]
1961 _ => false,
1962 }
1963}
1964
1965#[allow(clippy::too_many_arguments)]
1967pub fn vae_qkv_attn_out(
1968 model: &Arc<CmfModel>,
1969 qkv_idx: usize,
1970 out_idx: usize,
1971 xn: &[f32],
1972 n: usize,
1973 dim: usize,
1974 nh: usize,
1975 hd: usize,
1976 scale: f32,
1977 angles: &[f32],
1978 eps: f32,
1979 qkv_bias: &[f32],
1980 proj: &mut [f32],
1981) -> bool {
1982 match backend() {
1983 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
1984 Backend::Wgpu => crate::gpu_wgpu::vae_qkv_attn_out(
1985 model, qkv_idx, out_idx, xn, n, dim, nh, hd, scale, angles, eps, qkv_bias, proj,
1986 ),
1987 #[allow(unreachable_patterns)]
1988 _ => false,
1989 }
1990}
1991
1992#[allow(clippy::too_many_arguments)]
1993pub fn vae_attention_packed(
1994 qkv: &[f32],
1995 nh: usize,
1996 n: usize,
1997 hd: usize,
1998 scale: f32,
1999 angles: &[f32],
2000 eps: f32,
2001 out: &mut [f32],
2002) -> bool {
2003 vae_attention_packed_layout(qkv, nh, n, hd, scale, angles, eps, out, 1)
2004}
2005
2006#[allow(clippy::too_many_arguments)]
2007pub fn vae_attention_packed_layout(
2008 qkv: &[f32],
2009 nh: usize,
2010 n: usize,
2011 hd: usize,
2012 scale: f32,
2013 angles: &[f32],
2014 eps: f32,
2015 out: &mut [f32],
2016 layout: u32,
2017) -> bool {
2018 match backend() {
2019 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
2020 Backend::Wgpu => crate::gpu_wgpu::vae_attention_packed_layout(
2021 qkv, nh, n, hd, scale, angles, eps, out, layout,
2022 ),
2023 #[allow(unreachable_patterns)]
2024 _ => false,
2025 }
2026}
2027
2028#[allow(clippy::too_many_arguments)]
2029pub fn dit_split_only(
2030 qkv: &[f32],
2031 nh: usize,
2032 n: usize,
2033 hd: usize,
2034 layout: u32,
2035 norm: Option<(&[f32], f32)>,
2036 out_q: &mut [f32],
2037) -> bool {
2038 match backend() {
2039 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
2040 Backend::Wgpu => crate::gpu_wgpu::dit_split_only(qkv, nh, n, hd, layout, norm, out_q),
2041 #[allow(unreachable_patterns)]
2042 _ => false,
2043 }
2044}
2045
2046pub fn gemm_nt_f32(x: &[f32], w: &[f32], y: &mut [f32], n: usize, k: usize, m: usize) -> bool {
2051 match backend() {
2052 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
2053 Backend::Wgpu => crate::gpu_wgpu::gemm_nt_f32(x, w, y, n, k, m),
2054 #[allow(unreachable_patterns)]
2055 _ => false,
2056 }
2057}
2058
2059#[allow(clippy::too_many_arguments)]
2062pub fn music3_ffn(
2063 model: &std::sync::Arc<CmfModel>,
2064 idx_in: usize,
2065 idx_out: usize,
2066 h: &[f32],
2067 bias_in: &[f32],
2068 n: usize,
2069 hs: usize,
2070 inter: usize,
2071 out: &mut [f32],
2072) -> bool {
2073 match backend() {
2074 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
2075 Backend::Wgpu => {
2076 crate::gpu_wgpu::music3_ffn(model, idx_in, idx_out, h, bias_in, n, hs, inter, out)
2077 }
2078 #[allow(unreachable_patterns)]
2079 _ => false,
2080 }
2081}
2082
2083#[allow(clippy::too_many_arguments)]
2087pub fn conv1d_gemm(
2088 x: &[f32],
2089 w: &[f32],
2090 ic: usize,
2091 oc: usize,
2092 n: usize,
2093 k: usize,
2094 pad: usize,
2095 dil: usize,
2096 out_n: usize,
2097 yt: &mut [f32],
2098) -> bool {
2099 match backend() {
2100 #[cfg(target_os = "macos")]
2101 Backend::Metal => crate::gpu_metal::conv1d_gemm(x, w, ic, oc, n, k, pad, dil, out_n, yt),
2102 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
2103 Backend::Wgpu => crate::gpu_wgpu::conv1d_gemm(x, w, ic, oc, n, k, pad, dil, out_n, yt),
2104 #[allow(unreachable_patterns)]
2105 _ => false,
2106 }
2107}
2108
2109#[allow(clippy::too_many_arguments)]
2111pub fn vae_conv2d_coop(
2112 w: &[f32],
2113 bias: Option<&[f32]>,
2114 x: &[f32],
2115 ic: usize,
2116 oc: usize,
2117 h: usize,
2118 wi: usize,
2119 k: usize,
2120 out: &mut [f32],
2121) -> bool {
2122 match backend() {
2123 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
2124 Backend::Wgpu => crate::gpu_wgpu::vae_conv2d_coop(w, bias, x, ic, oc, h, wi, k, out),
2125 #[allow(unreachable_patterns)]
2126 _ => false,
2127 }
2128}
2129
2130pub fn dit_attention_packed(
2131 qkv: &[f32],
2132 nh: usize,
2133 n: usize,
2134 hd: usize,
2135 scale: f32,
2136 nr: Option<(&[f32], &[f32], &[f32], f32)>,
2139 out: &mut [f32],
2140) -> bool {
2141 match backend() {
2142 #[cfg(feature = "gpu")]
2149 Backend::Wgpu => crate::gpu_wgpu::dit_attention_packed(qkv, nh, n, hd, scale, nr, out),
2150 #[allow(unreachable_patterns)]
2151 _ => false,
2152 }
2153}
2154
2155pub fn dit_attention_packed_available() -> bool {
2163 #[allow(unreachable_patterns)]
2164 match backend() {
2165 #[cfg(feature = "gpu")]
2166 Backend::Wgpu => crate::gpu_wgpu::dit_attention_packed_ready(),
2167 _ => false,
2168 }
2169}
2170
2171pub fn dit_attention(
2172 qh: &[f32],
2173 kh: &[f32],
2174 vh: &[f32],
2175 nh: usize,
2176 nkv: usize,
2177 n: usize,
2178 hd: usize,
2179 scale: f32,
2180 out: &mut [f32],
2181) -> bool {
2182 match backend() {
2183 #[cfg(target_os = "macos")]
2184 Backend::Metal => crate::gpu_metal::dit_attention(qh, kh, vh, nh, nkv, n, hd, scale, out),
2185 #[cfg(feature = "gpu")]
2186 Backend::Wgpu => crate::gpu_wgpu::dit_attention(qh, kh, vh, nh, nkv, n, hd, scale, out),
2187 #[allow(unreachable_patterns)]
2188 _ => false,
2189 }
2190}
2191
2192#[allow(unused_variables)]
2197pub fn q4tp_matmat(
2198 model: &Arc<CmfModel>,
2199 idx: usize,
2200 xs: &[f32],
2201 b: usize,
2202 rows: usize,
2203 cols: usize,
2204 out: &mut [f32],
2205) -> bool {
2206 match backend() {
2207 #[cfg(target_os = "macos")]
2208 Backend::Metal => crate::gpu_metal::q4tp_matmat(model, idx, xs, b, rows, cols, out),
2209 #[cfg(feature = "gpu")]
2210 Backend::Wgpu => crate::gpu_wgpu::q4tp_matmat(model, idx, xs, b, rows, cols, out),
2211 #[allow(unreachable_patterns)]
2212 _ => false,
2213 }
2214}
2215
2216pub fn q2tp_matmat(
2219 model: &Arc<CmfModel>,
2220 idx: usize,
2221 xs: &[f32],
2222 b: usize,
2223 rows: usize,
2224 cols: usize,
2225 out: &mut [f32],
2226) -> bool {
2227 match backend() {
2228 #[cfg(feature = "gpu")]
2229 Backend::Wgpu => crate::gpu_wgpu::q2tp_matmat(model, idx, xs, b, rows, cols, out),
2230 #[allow(unreachable_patterns)]
2231 _ => false,
2232 }
2233}
2234
2235pub fn q4tp_matvec(
2240 model: &Arc<CmfModel>,
2241 idx: usize,
2242 xs: &[f32],
2243 rows: usize,
2244 cols: usize,
2245 out: &mut [f32],
2246) -> bool {
2247 match backend() {
2248 #[cfg(target_os = "macos")]
2249 Backend::Metal => crate::gpu_metal::q4tp_matvec_for_test(model, idx, xs, rows, cols, out),
2250 #[cfg(feature = "gpu")]
2251 Backend::Wgpu => crate::gpu_wgpu::q4tp_matvec(model, idx, xs, rows, cols, out),
2252 #[allow(unreachable_patterns)]
2253 _ => false,
2254 }
2255}
2256
2257pub fn q4t_matvec(
2263 model: &Arc<CmfModel>,
2264 idx: usize,
2265 xs: &[f32],
2266 rows: usize,
2267 cols: usize,
2268 out: &mut [f32],
2269) -> bool {
2270 match backend() {
2271 #[cfg(target_os = "macos")]
2272 Backend::Metal => crate::gpu_metal::q4t_matvec_for_test(model, idx, xs, rows, cols, out),
2273 #[allow(unreachable_patterns)]
2274 _ => false,
2275 }
2276}
2277
2278pub fn q4t_matmat(
2279 model: &Arc<CmfModel>,
2280 idx: usize,
2281 xs: &[f32],
2282 b: usize,
2283 rows: usize,
2284 cols: usize,
2285 out: &mut [f32],
2286) -> bool {
2287 match backend() {
2288 #[cfg(target_os = "macos")]
2289 Backend::Metal => crate::gpu_metal::q4t_matmat(model, idx, xs, b, rows, cols, out),
2290 #[cfg(feature = "gpu")]
2291 Backend::Wgpu => crate::gpu_wgpu::q4t_matmat(model, idx, xs, b, rows, cols, out),
2292 #[allow(unreachable_patterns)]
2293 _ => false,
2294 }
2295}
2296
2297#[cfg(target_os = "macos")]
2299pub use crate::gpu_metal::{
2300 AttnDeviceParams, AttnGpuLayer, GdnGpuCfg, GdnGpuLayer, GpuMoe, GraphDims, MetalFfn,
2301 O1AttnParams, TokenGraph, kv_mirror_drop, kv_mirror_read_last, kv_mirror_take_imp,
2302};
2303
2304#[cfg(target_os = "macos")]
2306pub fn gdn_block(
2307 model: &Arc<CmfModel>,
2308 layers: &[GdnGpuLayer],
2309 states: &mut [&mut [f32]],
2310 cfg: &GdnGpuCfg,
2311 h: &mut [f32],
2312) -> bool {
2313 match backend() {
2314 Backend::Metal => crate::gpu_metal::gdn_block(model, layers, states, cfg, h),
2315 _ => false,
2316 }
2317}
2318
2319#[allow(unused_variables)]
2321pub fn moe_block(model: &Arc<CmfModel>, jobs: &[MoeJob], out: &mut [f32]) -> bool {
2322 match backend() {
2323 #[cfg(target_os = "macos")]
2324 Backend::Metal => crate::gpu_metal::moe_block(model, jobs, out),
2325 #[cfg(feature = "gpu")]
2326 Backend::Wgpu => crate::gpu_wgpu::moe_block(model, jobs, out),
2327 Backend::None => false,
2328 }
2329}
2330
2331#[allow(unused_variables)]
2333pub fn matvec_batch(model: &Arc<CmfModel>, jobs: &[BatchJob], out: &mut [&mut [f32]]) -> bool {
2334 match backend() {
2335 #[cfg(target_os = "macos")]
2336 Backend::Metal => crate::gpu_metal::matvec_batch(model, jobs, out),
2337 #[cfg(feature = "gpu")]
2338 Backend::Wgpu => crate::gpu_wgpu::matvec_batch(model, jobs, out),
2339 Backend::None => false,
2340 }
2341}
2342
2343static GRAPH_RACE_STATE: AtomicU8 = AtomicU8::new(0); static GRAPH_RACE_FLIP: AtomicU32 = AtomicU32::new(0);
2359static GRAPH_RACE_ARM_GRAPH: AtomicU8 = AtomicU8::new(0); static GRAPH_RACE_TOK: AtomicU32 = AtomicU32::new(0); static GRAPH_NS: [AtomicU64; 2] = [AtomicU64::new(0), AtomicU64::new(0)]; static GRAPH_N: [AtomicU32; 2] = [AtomicU32::new(0), AtomicU32::new(0)];
2363
2364const GRAPH_RACE_SAMPLES: u32 = 4;
2366
2367static GRAPH_UNSUPPORTED: AtomicBool = AtomicBool::new(false);
2377
2378pub fn graph_mark_unsupported() {
2383 if !GRAPH_UNSUPPORTED.swap(true, Ordering::Relaxed) {
2384 tracing::info!("wgpu token graph: unsupported for this model — not retrying");
2385 }
2386}
2387
2388pub fn graph_unsupported() -> bool {
2389 GRAPH_UNSUPPORTED.load(Ordering::Relaxed)
2390}
2391
2392pub fn graph_unsupported_reset() {
2394 GRAPH_UNSUPPORTED.store(false, Ordering::Relaxed);
2395}
2396
2397pub fn graph_race_begin_generation() {
2398 #[cfg(feature = "gpu")]
2403 {
2404 static FLUSHED: std::sync::Once = std::sync::Once::new();
2416 static FIRST: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true);
2417 if FIRST.swap(false, Ordering::Relaxed) {
2418 } else {
2420 FLUSHED.call_once(crate::gpu_wgpu::pipeline_cache_flush);
2421 }
2422 }
2423 GRAPH_RACE_TOK.store(0, Ordering::Relaxed);
2424 if GRAPH_RACE_STATE.load(Ordering::Relaxed) != 0 {
2425 return;
2426 }
2427 let (gn, cn) = (
2428 GRAPH_N[1].load(Ordering::Relaxed),
2429 GRAPH_N[0].load(Ordering::Relaxed),
2430 );
2431 if gn >= GRAPH_RACE_SAMPLES && cn >= GRAPH_RACE_SAMPLES {
2432 let g_avg = GRAPH_NS[1].load(Ordering::Relaxed) / gn as u64;
2433 let c_avg = GRAPH_NS[0].load(Ordering::Relaxed) / cn as u64;
2434 let verdict = if g_avg < c_avg { 1 } else { 2 };
2435 GRAPH_RACE_STATE.store(verdict, Ordering::Relaxed);
2436 tracing::info!(
2437 "wgpu graph race: graph {:.2} ms/tok vs normal {:.2} ms/tok -> {}",
2438 g_avg as f64 / 1e6,
2439 c_avg as f64 / 1e6,
2440 if verdict == 1 { "graph" } else { "normal path" }
2441 );
2442 return;
2443 }
2444 let flip = GRAPH_RACE_FLIP.fetch_add(1, Ordering::Relaxed);
2445 GRAPH_RACE_ARM_GRAPH.store((flip % 2 == 1) as u8, Ordering::Relaxed);
2446}
2447
2448pub fn graph_race_use_graph(trusted: bool) -> bool {
2452 if trusted {
2453 return true;
2454 }
2455 match GRAPH_RACE_STATE.load(Ordering::Relaxed) {
2456 1 => true,
2457 2 => false,
2458 _ => GRAPH_RACE_ARM_GRAPH.load(Ordering::Relaxed) == 1,
2459 }
2460}
2461
2462pub fn graph_race_first_token_hopeless(dur: std::time::Duration) -> bool {
2467 if GRAPH_RACE_STATE.load(Ordering::Relaxed) != 0 {
2468 return false;
2469 }
2470 let first = GRAPH_RACE_TOK.load(Ordering::Relaxed) == 0;
2471 let cn = GRAPH_N[0].load(Ordering::Relaxed);
2472 if !first || cn == 0 {
2473 return false;
2474 }
2475 let c_avg = GRAPH_NS[0].load(Ordering::Relaxed) / cn as u64;
2476 let ns = dur.as_nanos() as u64;
2477 if ns > 1_000_000_000 && ns > 4 * c_avg {
2478 GRAPH_RACE_STATE.store(2, Ordering::Relaxed);
2479 tracing::info!(
2480 "wgpu graph race: first graph token {:.0} ms vs normal {:.2} ms/tok — hopeless, normal path wins",
2481 ns as f64 / 1e6,
2482 c_avg as f64 / 1e6
2483 );
2484 return true;
2485 }
2486 false
2487}
2488
2489pub fn graph_race_record(used_graph: bool, dur: std::time::Duration) {
2493 if GRAPH_RACE_STATE.load(Ordering::Relaxed) != 0 {
2494 return;
2495 }
2496 let tok = GRAPH_RACE_TOK.fetch_add(1, Ordering::Relaxed);
2497 if tok == 0 {
2498 return;
2499 }
2500 let i = used_graph as usize;
2501 GRAPH_NS[i].fetch_add(dur.as_nanos() as u64, Ordering::Relaxed);
2502 GRAPH_N[i].fetch_add(1, Ordering::Relaxed);
2503}
2504
2505pub(crate) fn fp_bytes(data: &[u8]) -> u64 {
2515 #[inline]
2516 fn fnv(mut h: u64, bytes: &[u8]) -> u64 {
2517 let (chunks, tail) = bytes.split_at(bytes.len() & !7);
2518 for c in chunks.chunks_exact(8) {
2519 h ^= u64::from_le_bytes(c.try_into().unwrap());
2520 h = h.wrapping_mul(0x100_0000_01b3);
2521 }
2522 for &b in tail {
2523 h ^= b as u64;
2524 h = h.wrapping_mul(0x100_0000_01b3);
2525 }
2526 h
2527 }
2528 let mut h = 0xcbf2_9ce4_8422_2325u64 ^ (data.len() as u64);
2529 if data.len() <= 4096 {
2530 return fnv(h, data);
2531 }
2532 let step = (data.len() - 64) / 63;
2533 for i in 0..64 {
2534 h = fnv(h, &data[i * step..i * step + 64]);
2535 }
2536 h
2537}
2538
2539pub(crate) fn fp_f32(data: &[f32]) -> u64 {
2542 let bytes = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4) };
2543 fp_bytes(bytes)
2544}
2545
2546#[cfg(test)]
2547mod fp_tests {
2548 use super::fp_bytes;
2549
2550 #[test]
2555 fn fp_bytes_sees_a_change_anywhere_in_a_sampled_slice() {
2556 let n = 1 << 20; let base: Vec<u8> = (0..n).map(|i| (i * 31 + 7) as u8).collect();
2558 let h0 = fp_bytes(&base);
2559 assert_eq!(h0, fp_bytes(&base), "fingerprint must be deterministic");
2560 let mut dense = base.clone();
2563 for b in dense.iter_mut() {
2564 *b = b.wrapping_add(1);
2565 }
2566 assert_ne!(
2567 h0,
2568 fp_bytes(&dense),
2569 "a fully different tensor slipped through"
2570 );
2571 assert_ne!(h0, fp_bytes(&base[..n - 64]));
2574 let mut small = vec![3u8; 4096];
2577 let hs = fp_bytes(&small);
2578 small[2048] ^= 1;
2579 assert_ne!(hs, fp_bytes(&small), "full hash missed a one-byte change");
2580 for n in [4097usize, 5000, 64 * 64, 1 << 16] {
2582 let v = vec![9u8; n];
2583 let _ = fp_bytes(&v); }
2585 }
2586}
2587
2588pub fn bake_release() {
2592 #[cfg(feature = "gpu")]
2593 crate::gpu_wgpu::bake_release();
2594}
2595
2596pub fn bake_precision_strict(on: bool) {
2600 #[cfg(feature = "gpu")]
2601 crate::gpu_wgpu::bake_precision_strict(on);
2602 #[cfg(not(feature = "gpu"))]
2603 let _ = on;
2604}
2605
2606pub fn hostprof_encode_done(t0: std::time::Instant) {
2612 use std::sync::atomic::{AtomicU64, Ordering};
2613 static ENC: AtomicU64 = AtomicU64::new(0);
2614 static N: AtomicU64 = AtomicU64::new(0);
2615 if std::env::var("CMF_GRAPH_HOSTPROF").as_deref() != Ok("1") {
2616 return;
2617 }
2618 ENC.fetch_add(t0.elapsed().as_nanos() as u64, Ordering::Relaxed);
2619 let n = N.fetch_add(1, Ordering::Relaxed) + 1;
2620 if n % 100 == 0 {
2621 eprintln!(
2622 "hostprof: encode {:.2} ms/token over {n} tokens",
2623 ENC.load(Ordering::Relaxed) as f64 / n as f64 / 1e6
2624 );
2625 }
2626}
2627
2628pub fn hostprof_total(t0: std::time::Instant) {
2629 use std::sync::atomic::{AtomicU64, Ordering};
2630 static TOT: AtomicU64 = AtomicU64::new(0);
2631 static N: AtomicU64 = AtomicU64::new(0);
2632 if std::env::var("CMF_GRAPH_HOSTPROF").as_deref() != Ok("1") {
2633 return;
2634 }
2635 TOT.fetch_add(t0.elapsed().as_nanos() as u64, Ordering::Relaxed);
2636 let n = N.fetch_add(1, Ordering::Relaxed) + 1;
2637 if n % 100 == 0 {
2638 eprintln!(
2639 "hostprof: total {:.2} ms/token over {n} tokens",
2640 TOT.load(Ordering::Relaxed) as f64 / n as f64 / 1e6
2641 );
2642 }
2643}
2644
2645pub fn stageprof(stage: u32, dt: std::time::Duration) {
2649 use std::sync::atomic::{AtomicU64, Ordering};
2650 static NS: [AtomicU64; 4] = [
2651 AtomicU64::new(0),
2652 AtomicU64::new(0),
2653 AtomicU64::new(0),
2654 AtomicU64::new(0),
2655 ];
2656 static N: AtomicU64 = AtomicU64::new(0);
2657 if std::env::var("CMF_GRAPH_HOSTPROF").as_deref() != Ok("1") {
2658 return;
2659 }
2660 NS[stage as usize % 4].fetch_add(dt.as_nanos() as u64, Ordering::Relaxed);
2661 if stage == 1 {
2662 let n = N.fetch_add(1, Ordering::Relaxed) + 1;
2663 if n % 200 == 0 {
2664 eprintln!(
2665 "stageprof: planning {:.2} ms/tok | gdn-item {:.2} ms/tok | attn-item {:.2} ms/tok ({n} tok)",
2666 NS[1].load(Ordering::Relaxed) as f64 / n as f64 / 1e6,
2667 NS[2].load(Ordering::Relaxed) as f64 / n as f64 / 1e6,
2668 NS[3].load(Ordering::Relaxed) as f64 / n as f64 / 1e6
2669 );
2670 }
2671 }
2672}
2673
2674pub fn weight_bytes_dispatched() -> u64 {
2677 let mut total = 0u64;
2678 #[cfg(target_os = "macos")]
2679 {
2680 total += crate::gpu_metal::WEIGHT_BYTES.load(std::sync::atomic::Ordering::Relaxed);
2681 }
2682 #[cfg(feature = "gpu")]
2683 {
2684 total += crate::gpu_wgpu::WEIGHT_BYTES.load(std::sync::atomic::Ordering::Relaxed);
2685 }
2686 total
2687}
2688
2689pub fn weight_bytes_by() -> [u64; 6] {
2692 #[cfg(target_os = "macos")]
2693 {
2694 let mut o = [0u64; 6];
2695 for (i, a) in crate::gpu_metal::WEIGHT_BYTES_BY.iter().enumerate() {
2696 o[i] = a.load(std::sync::atomic::Ordering::Relaxed);
2697 }
2698 return o;
2699 }
2700 #[allow(unreachable_code)]
2701 [0; 6]
2702}