use cortiq_core::CmfModel;
use std::cell::Cell;
use std::sync::{Arc, OnceLock};
thread_local! {
static CUR_LAYER: Cell<i64> = const { Cell::new(-1) };
}
pub fn set_layer(l: i64) {
CUR_LAYER.with(|c| c.set(l));
}
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 {
enabled() && layer_allowed()
}
pub const GPU_MIN_ROWS: usize = 65_536;
pub fn min_rows() -> usize {
std::env::var("CMF_GPU_MIN_ROWS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(GPU_MIN_ROWS)
}
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 struct BatchJob<'a> {
pub idx: usize,
pub rows: usize,
pub cols: usize,
pub row_scale: &'a [f32],
pub xs: Vec<f32>,
}
#[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 enabled() -> bool {
backend() != Backend::None
}
#[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 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,
}
}