mod context;
mod gemm;
mod map;
mod pool;
use std::sync::OnceLock;
use crate::backend::BackendUnavailable;
use crate::{GemmTask, MapOperation};
use self::context::{Context, SetupError};
const FLOP_THRESHOLD: usize = 1 << 25;
#[cfg(all(feature = "accelerate", target_os = "macos"))]
const MAP_THRESHOLD: usize = 1 << 19;
#[cfg(not(all(feature = "accelerate", target_os = "macos")))]
const MAP_THRESHOLD: usize = 1 << 17;
static CONTEXT: OnceLock<Result<Context, SetupError>> = OnceLock::new();
static POISON: OnceLock<String> = OnceLock::new();
fn initialized() -> &'static Result<Context, SetupError> {
CONTEXT.get_or_init(Context::new)
}
fn context() -> Result<&'static Context, BackendUnavailable> {
if let Some(reason) = POISON.get() {
return Err(BackendUnavailable::Poisoned(reason.clone()));
}
match initialized() {
Ok(context) => Ok(context),
Err(error) => Err(BackendUnavailable::Initialization(error.to_string())),
}
}
pub(super) fn status() -> Result<(), BackendUnavailable> {
context().map(|_| ())
}
pub(super) fn gemm_f32(task: &GemmTask<'_, f32>) -> Option<Vec<f32>> {
let flops = 2usize
.saturating_mul(task.m())
.saturating_mul(task.n())
.saturating_mul(task.k());
if flops < FLOP_THRESHOLD {
return None;
}
if task.m() == 1 || task.n() == 1 {
return None;
}
if !fits_u32(task) {
return None;
}
let context = context().ok()?;
match gemm::executed(context, task, gemm::Kernel::Specialized) {
Ok(product) => Some(product),
Err(reason) => {
let _ = POISON.set(reason);
None
}
}
}
pub(super) fn map_f32(operation: MapOperation, elements: &[f32]) -> Option<Vec<f32>> {
if elements.len() < MAP_THRESHOLD || elements.len() > u32::MAX as usize {
return None;
}
let context = context().ok()?;
match map::executed(context, operation, elements) {
Ok(mapped) => Some(mapped),
Err(reason) => {
let _ = POISON.set(reason);
None
}
}
}
fn fits_u32(task: &GemmTask<'_, f32>) -> bool {
let limit = u32::MAX as usize;
task.m() <= limit
&& task.n() <= limit
&& task.k() <= limit
&& task.a_strides().iter().all(|&stride| stride <= limit)
&& task.b_strides().iter().all(|&stride| stride <= limit)
}
#[cfg(test)]
#[path = "tests/metal_tests.rs"]
mod tests;