use cortiq_core::CmfModel;
use std::cell::Cell;
use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicU64, Ordering};
use std::sync::{Arc, OnceLock};
thread_local! {
static CUR_LAYER: Cell<i64> = const { Cell::new(-1) };
static CPU_ONLY: Cell<bool> = const { Cell::new(false) };
static PROBE_COLD: Cell<bool> = const { Cell::new(false) };
}
pub fn cpu_scope<R>(f: impl FnOnce() -> R) -> R {
struct Restore(bool);
impl Drop for Restore {
fn drop(&mut self) {
CPU_ONLY.with(|c| c.set(self.0));
}
}
let previous = CPU_ONLY.with(|c| c.replace(true));
let _restore = Restore(previous);
f()
}
pub fn probe_set_device(label: &str) {
let _ = DEVICE_LABEL.set(label.to_string());
}
fn device_label() -> &'static str {
DEVICE_LABEL.get().map(String::as_str).unwrap_or("unknown")
}
static DEVICE_LABEL: std::sync::OnceLock<String> = std::sync::OnceLock::new();
static CACHE_DIR: std::sync::OnceLock<std::path::PathBuf> = std::sync::OnceLock::new();
pub fn set_cache_dir(dir: std::path::PathBuf) {
let _ = CACHE_DIR.set(dir);
}
pub fn cache_dir_pub() -> std::path::PathBuf {
cache_dir()
}
fn cache_dir() -> std::path::PathBuf {
if let Some(d) = CACHE_DIR.get() {
return d.clone();
}
match std::env::var_os("TMPDIR") {
Some(t) => std::path::PathBuf::from(t),
None => std::env::temp_dir(),
}
}
fn probe_cache_path() -> Option<std::path::PathBuf> {
match std::env::var("CMF_PROBE_CACHE") {
Ok(v) if v == "0" => None,
Ok(v) => Some(std::path::PathBuf::from(v)),
Err(_) => Some(cache_dir().join("cortiq-gpu-probe.tsv")),
}
}
fn probe_cache_key_named(class: &str) -> String {
format!("{}\t{}\t{}", env!("CARGO_PKG_VERSION"), device_label(), class)
}
const CLASS_NAMES: [&str; 7] = [
"ffn",
"matvec",
"matmat",
"qkv-batch",
"matmat-wide",
"lm-head",
"gemm-nt",
];
fn probe_cache_load() {
static ONCE: std::sync::Once = std::sync::Once::new();
ONCE.call_once(|| {
let Some(path) = probe_cache_path() else {
return;
};
if cfg!(test) && std::env::var("CMF_PROBE_CACHE").is_err() {
return;
}
let Ok(text) = std::fs::read_to_string(&path) else {
return;
};
probe_cache_adopt(&text);
});
}
fn probe_cache_adopt(text: &str) {
for line in text.lines() {
let Some((key, verdict)) = line.rsplit_once('\t') else {
continue;
};
let winner = match verdict.trim() {
"gpu" => 1u8,
"cpu" => 2u8,
_ => continue,
};
for (i, name) in CLASS_NAMES.iter().enumerate() {
if probe_cache_key_named(name) == key {
let _ =
PROBES[i]
.state
.compare_exchange(0, winner, Ordering::Relaxed, Ordering::Relaxed);
tracing::debug!("gpu probe [{name}]: remembered → {verdict}");
}
}
}
}
fn probe_cache_store(c: OpClass, winner: u8) {
let Some(path) = probe_cache_path() else {
return;
};
let line = format!(
"{}\t{}\n",
probe_cache_key_named(CLASS_NAMES[c as usize]),
if winner == 1 { "gpu" } else { "cpu" }
);
use std::io::Write;
if let Ok(mut f) = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&path)
{
let _ = f.write_all(line.as_bytes());
}
}
pub(crate) fn probe_note_cold() {
PROBE_COLD.with(|c| c.set(true));
}
pub(crate) fn probe_was_cold() -> bool {
PROBE_COLD.with(|c| c.get())
}
pub fn set_layer(l: i64) {
CUR_LAYER.with(|c| c.set(l));
}
pub fn cur_layer() -> i64 {
CUR_LAYER.with(|c| c.get())
}
fn layer_ranges() -> &'static Option<Vec<(i64, i64)>> {
static R: OnceLock<Option<Vec<(i64, i64)>>> = OnceLock::new();
R.get_or_init(|| {
let s = std::env::var("CMF_GPU_LAYERS").ok()?;
let mut v = Vec::new();
for part in s.split(',') {
let part = part.trim();
match part.split_once('-') {
Some((a, b)) => v.push((a.trim().parse().ok()?, b.trim().parse().ok()?)),
None => {
let x: i64 = part.parse().ok()?;
v.push((x, x));
}
}
}
Some(v)
})
}
fn layer_allowed() -> bool {
match layer_ranges() {
None => true,
Some(ranges) => {
let cur = CUR_LAYER.with(|c| c.get());
cur < 0 || ranges.iter().any(|(a, b)| cur >= *a && cur <= *b)
}
}
}
pub fn enabled_here() -> bool {
!CPU_ONLY.with(|c| c.get()) && enabled() && layer_allowed()
}
#[derive(Clone, Copy)]
pub enum OpClass {
Ffn = 0,
Matvec = 1,
Matmat = 2,
Batch = 3,
MatmatWide = 4,
MatvecHead = 5,
GemmNt = 6,
}
pub fn matvec_class(rows: usize, cols: usize) -> OpClass {
if rows * cols >= 67_108_864 {
OpClass::MatvecHead
} else {
OpClass::Matvec
}
}
pub enum ProbeArm {
Gpu,
CpuTimed,
Cpu,
}
const PROBE_SAMPLES: u32 = 6;
struct Probe {
state: AtomicU8,
flip: AtomicU32,
gpu_ns: AtomicU64,
gpu_n: AtomicU32,
cpu_ns: AtomicU64,
cpu_n: AtomicU32,
gpu_min: AtomicU64,
cpu_min: AtomicU64,
}
impl Probe {
const fn new() -> Self {
Self {
state: AtomicU8::new(0),
flip: AtomicU32::new(0),
gpu_ns: AtomicU64::new(0),
gpu_n: AtomicU32::new(0),
cpu_ns: AtomicU64::new(0),
cpu_n: AtomicU32::new(0),
gpu_min: AtomicU64::new(u64::MAX),
cpu_min: AtomicU64::new(u64::MAX),
}
}
}
static PROBES: [Probe; 7] = [
Probe::new(),
Probe::new(),
Probe::new(),
Probe::new(),
Probe::new(),
Probe::new(),
Probe::new(),
];
fn probe_on() -> bool {
static ON: OnceLock<bool> = OnceLock::new();
*ON.get_or_init(|| {
std::env::var("CMF_GPU_PROBE")
.map(|v| v != "0" && v != "off")
.unwrap_or(true)
})
}
pub fn q1_force() -> bool {
#[cfg(target_os = "macos")]
{
backend() == Backend::Metal
}
#[cfg(not(target_os = "macos"))]
{
false
}
}
pub fn fused_block_trusted() -> bool {
#[cfg(target_os = "macos")]
if backend() == Backend::Metal {
return true;
}
wgpu_graph_default()
}
pub fn weight_is_resident(model: &Arc<CmfModel>, idx: usize) -> bool {
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
{
return crate::gpu_wgpu::weight_is_resident(model, idx);
}
#[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
{
let _ = (model, idx);
true
}
}
pub fn probe_arm_cold_prefers_gpu(c: OpClass, weights_resident: bool) -> ProbeArm {
if !weights_resident && probe_deciding(c) {
return ProbeArm::Gpu;
}
probe_arm(c)
}
pub fn probe_arm(c: OpClass) -> ProbeArm {
PROBE_COLD.with(|f| f.set(false));
if !probe_on() {
return ProbeArm::Gpu;
}
probe_cache_load();
let p = &PROBES[c as usize];
match p.state.load(Ordering::Relaxed) {
1 => ProbeArm::Gpu,
2 => ProbeArm::Cpu,
_ => {
if p.flip.fetch_add(1, Ordering::Relaxed) % 2 == 0 {
ProbeArm::Gpu
} else {
ProbeArm::CpuTimed
}
}
}
}
pub fn probe_record(c: OpClass, gpu: bool, dur: std::time::Duration) {
let p = &PROBES[c as usize];
if p.state.load(Ordering::Relaxed) != 0 {
return;
}
if gpu && PROBE_COLD.with(|f| f.replace(false)) {
return; }
let ns = dur.as_nanos().min(u64::MAX as u128) as u64;
if gpu {
p.gpu_ns.fetch_add(ns, Ordering::Relaxed);
p.gpu_n.fetch_add(1, Ordering::Relaxed);
p.gpu_min.fetch_min(ns, Ordering::Relaxed);
} else {
p.cpu_ns.fetch_add(ns, Ordering::Relaxed);
p.cpu_n.fetch_add(1, Ordering::Relaxed);
p.cpu_min.fetch_min(ns, Ordering::Relaxed);
}
let (gn, cn) = (
p.gpu_n.load(Ordering::Relaxed),
p.cpu_n.load(Ordering::Relaxed),
);
if gn >= 2 && cn >= 2 {
let g = p.gpu_min.load(Ordering::Relaxed) as f64;
let cp = p.cpu_min.load(Ordering::Relaxed) as f64;
if (gn < PROBE_SAMPLES || cn < PROBE_SAMPLES) && g < cp * 2.0 && cp < g * 2.0 {
return;
}
let winner = if g <= cp { 1 } else { 2 };
if p.state
.compare_exchange(0, winner, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
tracing::info!(
"gpu probe [{}]: gpu {:.2} ms vs cpu {:.2} ms per op → {}",
CLASS_NAMES[c as usize],
g / 1e6,
cp / 1e6,
if winner == 1 { "gpu" } else { "cpu" },
);
probe_cache_store(c, winner);
}
}
}
pub fn probe_deciding(c: OpClass) -> bool {
probe_on() && PROBES[c as usize].state.load(Ordering::Relaxed) == 0
}
#[allow(unused_variables)]
pub fn q8_resident_or_upload(model: &Arc<CmfModel>, idx: usize) -> bool {
static PROBE_UPLOADS: AtomicU32 = AtomicU32::new(0);
let may_upload = PROBE_UPLOADS.load(Ordering::Relaxed) < 4;
let resident = match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::q8_resident_or_upload(model, idx, may_upload),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q8_resident_or_upload(model, idx, may_upload),
Backend::None => false,
};
if !resident && may_upload {
PROBE_UPLOADS.fetch_add(1, Ordering::Relaxed);
}
resident
}
#[cfg(test)]
pub(crate) fn probe_reset() {
for p in &PROBES {
p.state.store(0, Ordering::Relaxed);
p.flip.store(0, Ordering::Relaxed);
p.gpu_ns.store(0, Ordering::Relaxed);
p.gpu_n.store(0, Ordering::Relaxed);
p.cpu_ns.store(0, Ordering::Relaxed);
p.cpu_n.store(0, Ordering::Relaxed);
}
}
#[cfg(test)]
mod probe_tests {
use super::*;
use std::time::Duration;
#[test]
fn probe_alternates_discards_cold_and_decides() {
probe_reset();
assert!(matches!(probe_arm(OpClass::Ffn), ProbeArm::Gpu));
assert!(matches!(probe_arm(OpClass::Ffn), ProbeArm::CpuTimed));
probe_note_cold();
probe_record(OpClass::Ffn, true, Duration::from_secs(1000));
for _ in 0..PROBE_SAMPLES {
probe_record(OpClass::Ffn, true, Duration::from_millis(1));
probe_record(OpClass::Ffn, false, Duration::from_millis(4));
}
assert!(matches!(probe_arm(OpClass::Ffn), ProbeArm::Gpu));
for _ in 0..PROBE_SAMPLES {
probe_record(OpClass::Matmat, true, Duration::from_millis(4));
probe_record(OpClass::Matmat, false, Duration::from_millis(1));
}
assert!(matches!(probe_arm(OpClass::Matmat), ProbeArm::Cpu));
cpu_scope(|| CPU_ONLY.with(|c| assert!(c.get())));
CPU_ONLY.with(|c| assert!(!c.get()));
cpu_scope(|| {
cpu_scope(|| CPU_ONLY.with(|c| assert!(c.get())));
CPU_ONLY.with(|c| assert!(c.get()));
});
let _ = std::panic::catch_unwind(|| cpu_scope(|| panic!("scope test")));
CPU_ONLY.with(|c| assert!(!c.get()));
probe_reset();
}
#[test]
fn a_remembered_verdict_is_adopted_and_a_stranger_is_not() {
let mine = probe_cache_key_named("gemm-nt");
let state = || PROBES[OpClass::GemmNt as usize].state.load(Ordering::Relaxed);
probe_cache_adopt("SomeOtherGPU/Vulkan\tgemm-nt\tgpu\n");
assert_eq!(state(), 0);
let older = mine.replacen(env!("CARGO_PKG_VERSION"), "0.0.0-old", 1);
assert_ne!(older, mine);
probe_cache_adopt(&format!("{older}\tgpu\n"));
assert_eq!(state(), 0);
probe_cache_adopt(&format!("{mine}\tcpu\n"));
assert_eq!(state(), 2);
PROBES[OpClass::GemmNt as usize]
.state
.store(0, Ordering::Relaxed);
}
}
pub const GPU_MIN_ROWS: usize = 65_536;
pub fn min_rows() -> usize {
if let Some(v) = std::env::var("CMF_GPU_MIN_ROWS")
.ok()
.and_then(|v| v.parse().ok())
{
return v;
}
if discrete() { 4096 } else { GPU_MIN_ROWS }
}
pub fn discrete() -> bool {
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::is_discrete(),
#[cfg(target_os = "macos")]
Backend::Metal => false, Backend::None => false,
}
}
pub struct MoeJob<'a> {
pub gate: (usize, usize, usize, &'a [f32]),
pub up: (usize, usize, usize, &'a [f32]),
pub down: (usize, usize, usize, &'a [f32]),
pub xs_gate: Vec<f32>,
pub xs_up: Vec<f32>,
pub down_col: &'a [f32],
pub w: f32,
pub q1: bool,
pub q4t: bool,
pub q4tp: bool,
pub gu_q2: bool,
pub swiglu_limit: f32,
}
pub struct BatchJob<'a> {
pub idx: usize,
pub rows: usize,
pub cols: usize,
pub row_scale: &'a [f32],
pub xs: Vec<f32>,
pub layout: BatchLayout,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BatchLayout {
Q8,
Q1,
Q4t,
Q4tp,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Backend {
None,
#[cfg(target_os = "macos")]
Metal,
#[cfg(feature = "gpu")]
Wgpu,
}
fn backend() -> Backend {
#[cfg(feature = "gpu")]
if crate::gpu_wgpu::selected() {
return if crate::gpu_wgpu::enabled() {
Backend::Wgpu
} else {
Backend::None
};
}
#[cfg(target_os = "macos")]
if crate::gpu_metal::enabled() {
return Backend::Metal;
}
Backend::None
}
pub fn backend_available() -> bool {
#[cfg(target_os = "macos")]
{
true
}
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
{
static AVAIL: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*AVAIL.get_or_init(crate::gpu_wgpu::adapter_probe)
}
#[cfg(all(not(feature = "gpu"), not(target_os = "macos")))]
{
false
}
}
pub fn enabled() -> bool {
backend() != Backend::None
}
pub fn wgpu_active() -> bool {
#[cfg(feature = "gpu")]
{
matches!(backend(), Backend::Wgpu)
}
#[cfg(not(feature = "gpu"))]
{
false
}
}
pub fn default_device() -> usize {
static D: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
*D.get_or_init(|| {
std::env::var("CMF_GPU_ADAPTER")
.ok()
.and_then(|v| v.trim().parse::<usize>().ok())
.unwrap_or(0)
})
}
thread_local! {
static CUR_DEV: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
}
pub fn current_device() -> usize {
CUR_DEV.with(|c| c.get()).unwrap_or_else(default_device)
}
pub fn set_current_device(i: usize) {
CUR_DEV.with(|c| c.set(Some(i)));
}
pub fn with_device<R>(dev: usize, f: impl FnOnce() -> R) -> R {
let prev = CUR_DEV.with(|c| c.replace(Some(dev)));
let r = f();
CUR_DEV.with(|c| c.set(prev));
r
}
pub fn device_count() -> usize {
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
{
return crate::gpu_wgpu::adapter_count();
}
#[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
{
usize::from(backend_available())
}
}
pub fn vram_budget() -> u64 {
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
{
return crate::gpu_wgpu::device_vram_budget();
}
#[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
{
if backend_available() { u64::MAX } else { 0 }
}
}
pub fn upload_bytes() -> u64 {
#[cfg(feature = "gpu")]
{
return crate::gpu_wgpu::UPLOAD_BYTES.load(std::sync::atomic::Ordering::Relaxed);
}
#[cfg(not(feature = "gpu"))]
0
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum GraphPhase {
Prefill,
Decode,
}
pub fn wgpu_graph_on(phase: GraphPhase) -> bool {
match std::env::var("CMF_GPU_WGPU_GRAPH").ok().as_deref() {
Some("0") => false,
Some("prefill") => phase == GraphPhase::Prefill,
Some(_) => true,
None => {
if wgpu_graph_default() {
return true;
}
let _ = phase;
false
}
}
}
pub fn wgpu_graph_default() -> bool {
#[cfg(feature = "gpu")]
{
matches!(backend(), Backend::Wgpu)
&& (crate::gpu_wgpu::discrete_active()
|| (cfg!(target_os = "macos") && crate::gpu_wgpu::adapter_up()))
}
#[cfg(not(feature = "gpu"))]
{
false
}
}
#[allow(clippy::too_many_arguments, unused_variables)]
pub fn q8_matvec_range(
model: &Arc<CmfModel>,
idx: usize,
row0: usize,
row_scale: &[f32],
xs: &[f32],
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => {
crate::gpu_metal::q8_matvec_range(model, idx, row0, row_scale, xs, rows, cols, out)
}
#[cfg(feature = "gpu")]
Backend::Wgpu => {
crate::gpu_wgpu::q8_matvec_range(model, idx, row0, row_scale, xs, rows, cols, out)
}
Backend::None => false,
}
}
#[allow(clippy::too_many_arguments, unused_variables)]
pub fn q8_matmat(
model: &Arc<CmfModel>,
idx: usize,
row_scale: &[f32],
pre: &[f32],
b: usize,
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => {
crate::gpu_metal::q8_matmat(model, idx, row_scale, pre, b, rows, cols, out)
}
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q8_matmat(model, idx, row_scale, pre, b, rows, cols, out),
Backend::None => false,
}
}
#[allow(unused_variables)]
pub fn q1_matvec(
model: &Arc<CmfModel>,
idx: usize,
xs: &[f32],
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::q1_matvec(model, idx, xs, rows, cols, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q1_matvec(model, idx, xs, rows, cols, out),
Backend::None => false,
}
}
#[allow(clippy::too_many_arguments)]
pub fn attn_dropin(
model: &Arc<CmfModel>,
kv_id: u64,
layer: usize,
normed: &[f32],
wq_idx: usize,
wk_idx: usize,
wv_idx: usize,
wo_idx: usize,
q_norm: Option<&[f32]>,
k_norm: Option<&[f32]>,
invf: &[f32],
nh: usize,
nkv: usize,
hd: usize,
rd: usize,
hidden: usize,
pos: usize,
cap: usize,
gemma: bool,
eps: f32,
cpu_k: &[Vec<f32>],
cpu_v: &[Vec<f32>],
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::attn_dropin_gpu(
model, kv_id, layer, normed, wq_idx, wk_idx, wv_idx, wo_idx, q_norm, k_norm, invf, nh,
nkv, hd, rd, hidden, pos, cap, gemma, eps, cpu_k, cpu_v, out,
),
#[allow(unused_variables)]
_ => false,
}
}
pub struct GraphW<'a> {
pub idx: usize,
pub kind: u8,
pub row_scale: &'a [f32],
pub data: &'a [f32],
}
pub enum GraphAttn<'a> {
Full {
wq: GraphW<'a>,
wk: GraphW<'a>,
wv: GraphW<'a>,
wo: GraphW<'a>,
q_norm: Option<&'a [f32]>,
k_norm: Option<&'a [f32]>,
bias: Option<(&'a [f32], &'a [f32], &'a [f32])>,
output_gate: bool,
cpu_k: &'a [Vec<f32>],
cpu_v: &'a [Vec<f32>],
},
Gdn {
qkv: GraphW<'a>,
z: GraphW<'a>,
a: GraphW<'a>,
b: GraphW<'a>,
out: GraphW<'a>,
conv1d: &'a [f32],
a_log: &'a [f32],
dt_bias: &'a [f32],
norm: &'a [f32],
nv: usize,
nk: usize,
dk: usize,
dv: usize,
kk: usize,
cpu_state: &'a [f32],
},
}
pub struct GraphLayer<'a> {
pub input_norm: &'a [f32],
pub attn: GraphAttn<'a>,
pub post_norm: &'a [f32],
pub ffn: GraphFfn<'a>,
}
pub enum GraphFfn<'a> {
Dense {
gate: GraphW<'a>,
up: GraphW<'a>,
down: GraphW<'a>,
},
Moe {
router: GraphW<'a>,
shared_gate: GraphW<'a>,
experts: Vec<(usize, usize, usize)>,
n_exp: usize,
top_k: usize,
inter: usize,
norm_topk: bool,
q4tp: bool,
gu_q2: bool,
},
}
#[allow(clippy::too_many_arguments)]
pub fn forward_token_graph(
model: &Arc<CmfModel>,
kv_id: u64,
layers: &[GraphLayer],
o1: &[Option<Vec<crate::nystrom::O1DeviceView<'_>>>],
o1_epoch: u64,
invf: &[f32],
h: &mut [f32],
nh: usize,
nkv: usize,
hd: usize,
rd: usize,
hidden: usize,
inter: usize,
position: usize,
cap: usize,
gemma: bool,
eps: f32,
lm_head: Option<(&GraphW, usize)>,
final_norm: &[f32],
logits: &mut Vec<f32>,
loop_norm_at: &[usize],
steps: usize,
embed: Option<(&GraphW, usize, f32)>,
ids_out: Option<&mut Vec<u32>>,
layers_run: Option<&mut usize>,
layer_base: usize,
) -> bool {
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::forward_token_graph(
model,
kv_id,
layers,
o1,
o1_epoch,
invf,
h,
nh,
nkv,
hd,
rd,
hidden,
inter,
position,
cap,
gemma,
eps,
lm_head,
final_norm,
logits,
loop_norm_at,
steps,
embed,
ids_out,
layers_run,
layer_base,
),
#[allow(unused_variables)]
_ => {
let _ = (lm_head, final_norm, logits, loop_norm_at, layers_run, layer_base);
false
}
}
}
pub struct SpecTail<'a> {
pub lm: GraphW<'a>,
pub lm_rows: usize,
pub final_norm: &'a [f32],
pub logits_out: &'a mut Vec<f32>,
}
#[allow(clippy::too_many_arguments)]
pub fn forward_batch_graph(
model: &Arc<CmfModel>,
kv_id: u64,
layers: &[GraphLayer],
invf: &[f32],
h: &mut [f32],
nh: usize,
nkv: usize,
hd: usize,
rd: usize,
hidden: usize,
inter: usize,
positions: &[usize],
cap: usize,
gemma: bool,
eps: f32,
k: usize,
spec: Option<SpecTail<'_>>,
) -> bool {
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::forward_batch_graph(
model, kv_id, layers, invf, h, nh, nkv, hd, rd, hidden, inter, positions, cap, gemma,
eps, k, spec,
),
#[allow(unreachable_patterns)]
_ => {
let _ = spec;
false
}
}
}
pub fn gdn_spec_restore(kv_id: u64, slot: usize) -> bool {
#[cfg(feature = "gpu")]
if backend() == Backend::Wgpu {
return crate::gpu_wgpu::gdn_spec_restore(kv_id, slot);
}
#[allow(unreachable_code)]
{
let _ = (kv_id, slot);
false
}
}
pub fn graph_kv_reset(_kv_id: u64) {
#[cfg(feature = "gpu")]
if backend() == Backend::Wgpu {
crate::gpu_wgpu::kv_mirror_reset(_kv_id);
}
}
pub fn q1t_matvec(
model: &Arc<CmfModel>,
idx: usize,
xs: &[f32],
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => {
if metal_q1t_enabled() {
crate::gpu_metal::q1t_matvec(model, idx, xs, rows, cols, out)
} else {
false
}
}
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q1t_matvec(model, idx, xs, rows, cols, out),
Backend::None => false,
}
}
#[allow(unused_variables)]
pub fn q4b_matvec(
model: &Arc<CmfModel>,
idx: usize,
xs: &[f32],
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => false,
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q4b_matvec(model, idx, xs, rows, cols, out),
Backend::None => false,
}
}
pub fn q1t_matmat(
model: &Arc<CmfModel>,
idx: usize,
xs: &[f32],
b: usize,
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::q1t_matmat(model, idx, xs, b, rows, cols, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q1t_matmat(model, idx, xs, b, rows, cols, out),
Backend::None => false,
}
}
#[cfg(target_os = "macos")]
pub(crate) fn metal_q1t_enabled() -> bool {
std::env::var("CMF_METAL_Q1T")
.map(|v| v != "0" && !v.eq_ignore_ascii_case("off"))
.unwrap_or(true)
}
pub fn q1_matmat(
model: &Arc<CmfModel>,
idx: usize,
xs: &[f32],
b: usize,
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q1_matmat(model, idx, xs, b, rows, cols, out),
#[allow(unused_variables)]
_ => false,
}
}
static MM_KILL: AtomicBool = AtomicBool::new(false);
pub(crate) fn mm_killed() -> bool {
MM_KILL.load(Ordering::Relaxed)
}
pub(crate) fn mm_kill() {
MM_KILL.store(true, Ordering::Relaxed);
}
#[allow(unused_variables, clippy::too_many_arguments)]
pub fn chunk_attend(
q: &[f32],
k: &[&[f32]],
v: &[&[f32]],
b: usize,
s0: usize,
nh: usize,
nkv: usize,
hd: usize,
scale: f32,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::chunk_attend(q, k, v, b, s0, nh, nkv, hd, scale, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(unused_variables, clippy::too_many_arguments)]
pub fn q4t_qkv(
model: &Arc<CmfModel>,
wq: usize,
wk: usize,
wv: usize,
xs: &[f32],
b: usize,
cols: usize,
rq: usize,
rk: usize,
rv: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q4t_qkv(model, wq, wk, wv, xs, b, cols, rq, rk, rv, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(unused_variables, clippy::too_many_arguments)]
#[allow(clippy::too_many_arguments, unused_variables)]
pub fn q4tp_ffn_packed(
model: &Arc<CmfModel>,
w1: usize,
w2: usize,
xs: &[f32],
b: usize,
hidden: usize,
inter: usize,
bias: Option<&[f32]>,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => {
crate::gpu_wgpu::q4tp_ffn_packed(model, w1, w2, xs, b, hidden, inter, bias, out)
}
#[allow(unreachable_patterns)]
_ => false,
}
}
pub fn q4tp_ffn(
model: &Arc<CmfModel>,
w1: usize,
w3: usize,
w2: usize,
xs: &[f32],
b: usize,
hidden: usize,
inter: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::q4tp_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q4tp_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
pub fn q4t_ffn(
model: &Arc<CmfModel>,
w1: usize,
w3: usize,
w2: usize,
xs: &[f32],
b: usize,
hidden: usize,
inter: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::q4t_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q4t_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
pub struct DitBlockArgs<'a> {
pub n: usize,
pub hidden: usize,
pub inter: usize,
pub nh: usize,
pub nkv: usize,
pub hd: usize,
pub eps: f32,
pub rope_cos: &'a [f32],
pub rope_sin: &'a [f32],
pub norm1: &'a [f32],
pub norm2: &'a [f32],
pub ffn_norm1: &'a [f32],
pub ffn_norm2: &'a [f32],
pub norm_q: &'a [f32],
pub norm_k: &'a [f32],
pub s_msa: &'a [f32],
pub gate_msa: &'a [f32],
pub s_mlp: &'a [f32],
pub gate_mlp: &'a [f32],
pub wq: usize,
pub wk: usize,
pub wv: usize,
pub wo: usize,
pub w1: usize,
pub w3: usize,
pub w2: usize,
pub q4tp: bool,
pub resident_in: bool,
pub resident_out: bool,
}
pub fn dit_chain_supported() -> bool {
#[cfg(feature = "gpu")]
{
return matches!(backend(), Backend::Wgpu) && fused_dit_block_available();
}
#[allow(unreachable_code)]
false
}
pub fn dit_state_fetch(_x: &mut [f32]) -> bool {
#[cfg(feature = "gpu")]
{
if matches!(backend(), Backend::Wgpu) {
return crate::gpu_wgpu::dit_state_fetch(_x);
}
}
false
}
#[allow(unused_variables)]
#[allow(unused_variables, clippy::too_many_arguments)]
pub fn dit_qkv(
model: &Arc<CmfModel>,
wq: usize,
wk: usize,
wv: usize,
xs: &[f32],
b: usize,
hidden: usize,
qrows: usize,
kvrows: usize,
q_out: &mut [f32],
k_out: &mut [f32],
v_out: &mut [f32],
) -> bool {
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q4tp_qkv(
model, wq, wk, wv, xs, b, hidden, qrows, kvrows, q_out, k_out, v_out,
),
#[allow(unreachable_patterns)]
_ => false,
}
}
pub fn fused_dit_block_available() -> bool {
#[cfg(target_os = "macos")]
{
matches!(backend(), Backend::Metal) && fused_block_trusted()
}
#[cfg(not(target_os = "macos"))]
{
false
}
}
pub fn dit_block(model: &Arc<CmfModel>, a: &DitBlockArgs, x: &mut [f32]) -> bool {
dit_block_seg(model, a, &[a.n], x)
}
pub fn dit_block_seg(
model: &Arc<CmfModel>,
a: &DitBlockArgs,
segs: &[usize],
x: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal if segs.len() <= 1 => crate::gpu_metal::dit_block(model, a, x),
#[cfg(feature = "gpu")]
Backend::Wgpu
if match std::env::var("CMF_DIT_FUSED").ok().as_deref() {
Some("0") => false,
Some(_) => true,
None => crate::gpu_wgpu::discrete_active(),
} =>
{
crate::gpu_wgpu::dit_block_seg(model, a, segs, x)
}
#[allow(unreachable_patterns)]
_ => false,
}
}
pub struct VaeResnetArgs<'a> {
pub groups: usize,
pub ic: usize,
pub oc: usize,
pub h: usize,
pub w: usize,
pub n1w: &'a [f32],
pub n1b: &'a [f32],
pub c1w: &'a [f32],
pub c1b: &'a [f32],
pub c1k: usize,
pub n2w: &'a [f32],
pub n2b: &'a [f32],
pub c2w: &'a [f32],
pub c2b: &'a [f32],
pub c2k: usize,
pub shortcut: Option<(&'a [f32], &'a [f32], usize)>,
}
#[allow(unused_variables)]
pub fn vae_resnet(a: &VaeResnetArgs, x: &[f32], out: &mut [f32]) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::vae_resnet(a, x, out),
_ => false,
}
}
#[allow(unused_variables, clippy::too_many_arguments)]
pub fn vae_upsample_conv(
w: &[f32],
bias: &[f32],
x: &[f32],
ic: usize,
oc: usize,
h: usize,
w_img: usize,
k: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::vae_upsample_conv(w, bias, x, ic, oc, h, w_img, k, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => {
crate::gpu_wgpu::vae_upsample_conv(w, bias, x, ic, oc, h, w_img, k, out)
}
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(unused_variables, clippy::too_many_arguments)]
pub fn vae_conv2d(
w: &[f32],
bias: &[f32],
x: &[f32],
ic: usize,
oc: usize,
h: usize,
w_img: usize,
k: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::vae_conv2d(w, bias, x, ic, oc, h, w_img, k, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::vae_conv2d(w, bias, x, ic, oc, h, w_img, k, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(unused_variables, clippy::too_many_arguments)]
#[allow(unused_variables)]
#[allow(clippy::too_many_arguments)]
#[allow(clippy::too_many_arguments, unused_variables)]
pub fn dit_qkv_attention(
model: &Arc<CmfModel>,
qkv_idx: usize,
xn: &[f32],
n: usize,
hidden: usize,
nh: usize,
hd: usize,
scale: f32,
nr: (&[f32], &[f32], &[f32], f32),
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
Backend::Wgpu => crate::gpu_wgpu::dit_qkv_attention(
model, qkv_idx, xn, n, hidden, nh, hd, scale, nr, out,
),
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(clippy::too_many_arguments)]
pub fn dit_qkv_attn_out(
model: &Arc<CmfModel>,
qkv_idx: usize,
out_idx: usize,
xn: &[f32],
n: usize,
hidden: usize,
nh: usize,
hd: usize,
scale: f32,
nr: (&[f32], &[f32], &[f32], f32),
proj: &mut [f32],
) -> bool {
match backend() {
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
Backend::Wgpu => crate::gpu_wgpu::dit_qkv_attn_out(
model, qkv_idx, out_idx, xn, n, hidden, nh, hd, scale, nr, proj,
),
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(clippy::too_many_arguments)]
pub fn vae_qkv_attn_out(
model: &Arc<CmfModel>,
qkv_idx: usize,
out_idx: usize,
xn: &[f32],
n: usize,
dim: usize,
nh: usize,
hd: usize,
scale: f32,
angles: &[f32],
eps: f32,
qkv_bias: &[f32],
proj: &mut [f32],
) -> bool {
match backend() {
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
Backend::Wgpu => crate::gpu_wgpu::vae_qkv_attn_out(
model, qkv_idx, out_idx, xn, n, dim, nh, hd, scale, angles, eps, qkv_bias, proj,
),
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(clippy::too_many_arguments)]
pub fn vae_attention_packed(
qkv: &[f32],
nh: usize,
n: usize,
hd: usize,
scale: f32,
angles: &[f32],
eps: f32,
out: &mut [f32],
) -> bool {
vae_attention_packed_layout(qkv, nh, n, hd, scale, angles, eps, out, 1)
}
#[allow(clippy::too_many_arguments)]
pub fn vae_attention_packed_layout(
qkv: &[f32],
nh: usize,
n: usize,
hd: usize,
scale: f32,
angles: &[f32],
eps: f32,
out: &mut [f32],
layout: u32,
) -> bool {
match backend() {
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
Backend::Wgpu => crate::gpu_wgpu::vae_attention_packed_layout(
qkv, nh, n, hd, scale, angles, eps, out, layout,
),
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(clippy::too_many_arguments)]
pub fn dit_split_only(
qkv: &[f32],
nh: usize,
n: usize,
hd: usize,
layout: u32,
norm: Option<(&[f32], f32)>,
out_q: &mut [f32],
) -> bool {
match backend() {
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
Backend::Wgpu => crate::gpu_wgpu::dit_split_only(qkv, nh, n, hd, layout, norm, out_q),
#[allow(unreachable_patterns)]
_ => false,
}
}
pub fn gemm_nt_f32(x: &[f32], w: &[f32], y: &mut [f32], n: usize, k: usize, m: usize) -> bool {
match backend() {
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
Backend::Wgpu => crate::gpu_wgpu::gemm_nt_f32(x, w, y, n, k, m),
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(clippy::too_many_arguments)]
pub fn music3_ffn(
model: &std::sync::Arc<CmfModel>,
idx_in: usize,
idx_out: usize,
h: &[f32],
bias_in: &[f32],
n: usize,
hs: usize,
inter: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
Backend::Wgpu => {
crate::gpu_wgpu::music3_ffn(model, idx_in, idx_out, h, bias_in, n, hs, inter, out)
}
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(clippy::too_many_arguments)]
pub fn conv1d_gemm(
x: &[f32],
w: &[f32],
ic: usize,
oc: usize,
n: usize,
k: usize,
pad: usize,
dil: usize,
out_n: usize,
yt: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => {
crate::gpu_metal::conv1d_gemm(x, w, ic, oc, n, k, pad, dil, out_n, yt)
}
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
Backend::Wgpu => {
crate::gpu_wgpu::conv1d_gemm(x, w, ic, oc, n, k, pad, dil, out_n, yt)
}
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(clippy::too_many_arguments)]
pub fn vae_conv2d_coop(
w: &[f32],
bias: Option<&[f32]>,
x: &[f32],
ic: usize,
oc: usize,
h: usize,
wi: usize,
k: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(all(feature = "gpu", not(target_os = "macos")))]
Backend::Wgpu => crate::gpu_wgpu::vae_conv2d_coop(w, bias, x, ic, oc, h, wi, k, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
pub fn dit_attention_packed(
qkv: &[f32],
nh: usize,
n: usize,
hd: usize,
scale: f32,
nr: Option<(&[f32], &[f32], &[f32], f32)>,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::dit_attention_packed(qkv, nh, n, hd, scale, nr, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
pub fn dit_attention_packed_available() -> bool {
#[allow(unreachable_patterns)]
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::dit_attention_packed_ready(),
_ => false,
}
}
pub fn dit_attention(
qh: &[f32],
kh: &[f32],
vh: &[f32],
nh: usize,
nkv: usize,
n: usize,
hd: usize,
scale: f32,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::dit_attention(qh, kh, vh, nh, nkv, n, hd, scale, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::dit_attention(qh, kh, vh, nh, nkv, n, hd, scale, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
#[allow(unused_variables)]
pub fn q4tp_matmat(
model: &Arc<CmfModel>,
idx: usize,
xs: &[f32],
b: usize,
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::q4tp_matmat(model, idx, xs, b, rows, cols, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q4tp_matmat(model, idx, xs, b, rows, cols, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
pub fn q2tp_matmat(
model: &Arc<CmfModel>,
idx: usize,
xs: &[f32],
b: usize,
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q2tp_matmat(model, idx, xs, b, rows, cols, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
pub fn q4tp_matvec(
model: &Arc<CmfModel>,
idx: usize,
xs: &[f32],
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::q4tp_matvec_for_test(model, idx, xs, rows, cols, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q4tp_matvec(model, idx, xs, rows, cols, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
pub fn q4t_matvec(
model: &Arc<CmfModel>,
idx: usize,
xs: &[f32],
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::q4t_matvec_for_test(model, idx, xs, rows, cols, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
pub fn q4t_matmat(
model: &Arc<CmfModel>,
idx: usize,
xs: &[f32],
b: usize,
rows: usize,
cols: usize,
out: &mut [f32],
) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::q4t_matmat(model, idx, xs, b, rows, cols, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::q4t_matmat(model, idx, xs, b, rows, cols, out),
#[allow(unreachable_patterns)]
_ => false,
}
}
#[cfg(target_os = "macos")]
pub use crate::gpu_metal::{
AttnDeviceParams, AttnGpuLayer, GdnGpuCfg, GdnGpuLayer, GpuMoe, GraphDims, MetalFfn,
TokenGraph, kv_mirror_drop, kv_mirror_read_last, kv_mirror_take_imp,
};
#[cfg(target_os = "macos")]
pub fn gdn_block(
model: &Arc<CmfModel>,
layers: &[GdnGpuLayer],
states: &mut [&mut [f32]],
cfg: &GdnGpuCfg,
h: &mut [f32],
) -> bool {
match backend() {
Backend::Metal => crate::gpu_metal::gdn_block(model, layers, states, cfg, h),
_ => false,
}
}
#[allow(unused_variables)]
pub fn moe_block(model: &Arc<CmfModel>, jobs: &[MoeJob], out: &mut [f32]) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::moe_block(model, jobs, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::moe_block(model, jobs, out),
Backend::None => false,
}
}
#[allow(unused_variables)]
pub fn matvec_batch(model: &Arc<CmfModel>, jobs: &[BatchJob], out: &mut [&mut [f32]]) -> bool {
match backend() {
#[cfg(target_os = "macos")]
Backend::Metal => crate::gpu_metal::matvec_batch(model, jobs, out),
#[cfg(feature = "gpu")]
Backend::Wgpu => crate::gpu_wgpu::matvec_batch(model, jobs, out),
Backend::None => false,
}
}
static GRAPH_RACE_STATE: AtomicU8 = AtomicU8::new(0); static GRAPH_RACE_FLIP: AtomicU32 = AtomicU32::new(0);
static 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)];
const GRAPH_RACE_SAMPLES: u32 = 4;
static GRAPH_UNSUPPORTED: AtomicBool = AtomicBool::new(false);
pub fn graph_mark_unsupported() {
if !GRAPH_UNSUPPORTED.swap(true, Ordering::Relaxed) {
tracing::info!("wgpu token graph: unsupported for this model — not retrying");
}
}
pub fn graph_unsupported() -> bool {
GRAPH_UNSUPPORTED.load(Ordering::Relaxed)
}
pub fn graph_unsupported_reset() {
GRAPH_UNSUPPORTED.store(false, Ordering::Relaxed);
}
pub fn graph_race_begin_generation() {
#[cfg(feature = "gpu")]
{
static FLUSHED: std::sync::Once = std::sync::Once::new();
static FIRST: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(true);
if FIRST.swap(false, Ordering::Relaxed) {
} else {
FLUSHED.call_once(crate::gpu_wgpu::pipeline_cache_flush);
}
}
GRAPH_RACE_TOK.store(0, Ordering::Relaxed);
if GRAPH_RACE_STATE.load(Ordering::Relaxed) != 0 {
return;
}
let (gn, cn) = (
GRAPH_N[1].load(Ordering::Relaxed),
GRAPH_N[0].load(Ordering::Relaxed),
);
if gn >= GRAPH_RACE_SAMPLES && cn >= GRAPH_RACE_SAMPLES {
let g_avg = GRAPH_NS[1].load(Ordering::Relaxed) / gn as u64;
let c_avg = GRAPH_NS[0].load(Ordering::Relaxed) / cn as u64;
let verdict = if g_avg < c_avg { 1 } else { 2 };
GRAPH_RACE_STATE.store(verdict, Ordering::Relaxed);
tracing::info!(
"wgpu graph race: graph {:.2} ms/tok vs normal {:.2} ms/tok -> {}",
g_avg as f64 / 1e6,
c_avg as f64 / 1e6,
if verdict == 1 { "graph" } else { "normal path" }
);
return;
}
let flip = GRAPH_RACE_FLIP.fetch_add(1, Ordering::Relaxed);
GRAPH_RACE_ARM_GRAPH.store((flip % 2 == 1) as u8, Ordering::Relaxed);
}
pub fn graph_race_use_graph(trusted: bool) -> bool {
if trusted {
return true;
}
match GRAPH_RACE_STATE.load(Ordering::Relaxed) {
1 => true,
2 => false,
_ => GRAPH_RACE_ARM_GRAPH.load(Ordering::Relaxed) == 1,
}
}
pub fn graph_race_first_token_hopeless(dur: std::time::Duration) -> bool {
if GRAPH_RACE_STATE.load(Ordering::Relaxed) != 0 {
return false;
}
let first = GRAPH_RACE_TOK.load(Ordering::Relaxed) == 0;
let cn = GRAPH_N[0].load(Ordering::Relaxed);
if !first || cn == 0 {
return false;
}
let c_avg = GRAPH_NS[0].load(Ordering::Relaxed) / cn as u64;
let ns = dur.as_nanos() as u64;
if ns > 1_000_000_000 && ns > 4 * c_avg {
GRAPH_RACE_STATE.store(2, Ordering::Relaxed);
tracing::info!(
"wgpu graph race: first graph token {:.0} ms vs normal {:.2} ms/tok — hopeless, normal path wins",
ns as f64 / 1e6,
c_avg as f64 / 1e6
);
return true;
}
false
}
pub fn graph_race_record(used_graph: bool, dur: std::time::Duration) {
if GRAPH_RACE_STATE.load(Ordering::Relaxed) != 0 {
return;
}
let tok = GRAPH_RACE_TOK.fetch_add(1, Ordering::Relaxed);
if tok == 0 {
return;
}
let i = used_graph as usize;
GRAPH_NS[i].fetch_add(dur.as_nanos() as u64, Ordering::Relaxed);
GRAPH_N[i].fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn fp_bytes(data: &[u8]) -> u64 {
#[inline]
fn fnv(mut h: u64, bytes: &[u8]) -> u64 {
let (chunks, tail) = bytes.split_at(bytes.len() & !7);
for c in chunks.chunks_exact(8) {
h ^= u64::from_le_bytes(c.try_into().unwrap());
h = h.wrapping_mul(0x100_0000_01b3);
}
for &b in tail {
h ^= b as u64;
h = h.wrapping_mul(0x100_0000_01b3);
}
h
}
let mut h = 0xcbf2_9ce4_8422_2325u64 ^ (data.len() as u64);
if data.len() <= 4096 {
return fnv(h, data);
}
let step = (data.len() - 64) / 63;
for i in 0..64 {
h = fnv(h, &data[i * step..i * step + 64]);
}
h
}
pub(crate) fn fp_f32(data: &[f32]) -> u64 {
let bytes =
unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4) };
fp_bytes(bytes)
}
#[cfg(test)]
mod fp_tests {
use super::fp_bytes;
#[test]
fn fp_bytes_sees_a_change_anywhere_in_a_sampled_slice() {
let n = 1 << 20; let base: Vec<u8> = (0..n).map(|i| (i * 31 + 7) as u8).collect();
let h0 = fp_bytes(&base);
assert_eq!(h0, fp_bytes(&base), "fingerprint must be deterministic");
let mut dense = base.clone();
for b in dense.iter_mut() {
*b = b.wrapping_add(1);
}
assert_ne!(h0, fp_bytes(&dense), "a fully different tensor slipped through");
assert_ne!(h0, fp_bytes(&base[..n - 64]));
let mut small = vec![3u8; 4096];
let hs = fp_bytes(&small);
small[2048] ^= 1;
assert_ne!(hs, fp_bytes(&small), "full hash missed a one-byte change");
for n in [4097usize, 5000, 64 * 64, 1 << 16] {
let v = vec![9u8; n];
let _ = fp_bytes(&v); }
}
}
pub fn bake_release() {
#[cfg(feature = "gpu")]
crate::gpu_wgpu::bake_release();
}
pub fn bake_precision_strict(on: bool) {
#[cfg(feature = "gpu")]
crate::gpu_wgpu::bake_precision_strict(on);
#[cfg(not(feature = "gpu"))]
let _ = on;
}