use crate::cpu::{AmxSupport, Avx512Support};
#[cfg(target_arch = "x86_64")]
use hermes_simd_core::numa::{current_numa_node, verify_numa_locality};
pub struct AdaptiveDispatcher;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DispatchDecision {
Amx,
Avx512,
AvxVnni,
Scalar,
}
#[cfg(target_arch = "x86_64")]
#[inline]
fn is_multi_numa() -> bool {
use std::sync::OnceLock;
static MULTI_NUMA: OnceLock<bool> = OnceLock::new();
*MULTI_NUMA.get_or_init(|| {
themis::CpuTopology::detect().is_some_and(|topology| topology.numa_nodes().len() > 1)
})
}
impl AdaptiveDispatcher {
pub fn select_backend<T: AmxSupport + Avx512Support>(
m: usize,
n: usize,
k: usize,
a_ptr: *const T,
a_len: usize,
b_ptr: *const T,
b_len: usize,
) -> DispatchDecision {
#[cfg(target_arch = "x86_64")]
{
let has_amx = <T as AmxSupport>::has_amx();
let has_avx512 = <T as Avx512Support>::has_avx512();
let is_session_active = hermes_simd_intrinsics::AmxSession::is_active();
let min_ops = if is_session_active { 2048 } else { 16384 };
let total_ops = m * n * k;
let is_too_small = (m < 16) || (n < 16) || (k < 32);
if has_amx && total_ops >= min_ops && !is_too_small {
if is_multi_numa() {
if let Some(curr_node) = current_numa_node() {
let a_local = verify_numa_locality(
a_ptr as *const u8,
a_len * core::mem::size_of::<T>(),
curr_node,
);
let b_local = verify_numa_locality(
b_ptr as *const u8,
b_len * core::mem::size_of::<T>(),
curr_node,
);
if !a_local || !b_local {
#[cfg(all(debug_assertions, feature = "std"))]
{
static WARNED: core::sync::atomic::AtomicBool =
core::sync::atomic::AtomicBool::new(false);
if !WARNED.load(core::sync::atomic::Ordering::Relaxed)
&& !WARNED.swap(true, core::sync::atomic::Ordering::Relaxed)
{
std::eprintln!(
"WARNING [hermes-simd]: Cross-node NUMA memory access \
detected (thread node: {curr_node}). Re-routing AMX → \
AVX-512 to mitigate latency."
);
}
}
if has_avx512 {
return DispatchDecision::Avx512;
}
return DispatchDecision::Scalar;
}
}
}
return DispatchDecision::Amx;
}
if has_avx512 {
return DispatchDecision::Avx512;
}
if crate::cpu::has_avx_vnni() {
return DispatchDecision::AvxVnni;
}
}
let _ = (m, n, k, a_ptr, a_len, b_ptr, b_len);
DispatchDecision::Scalar
}
}