use himada_core::HardwareDNA;
#[cfg(target_arch = "aarch64")]
pub fn argmax_f64_neon(a: &[f64]) -> usize {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len();
if n == 0 { return 0; }
let mut i = 0;
let mut vmax = unsafe { vdupq_n_f64(f64::NEG_INFINITY) };
let mut vidx: float64x2_t = unsafe { std::mem::transmute::<[u64; 2], _>([0, 1]) };
unsafe {
while i + 2 <= n {
let va = vld1q_f64(a.as_ptr().add(i));
let vcmp = vcgtq_f64(va, vmax);
vmax = vmaxq_f64(vmax, va);
let local_idx: float64x2_t = std::mem::transmute::<[u64; 2], _>([i as u64, (i + 1) as u64]);
vidx = vbslq_f64(vcmp, local_idx, vidx);
i += 2;
}
}
let mut best_idx: usize = 0;
let mut best_val = f64::NEG_INFINITY;
let final_vals: [f64; 2] = unsafe { std::mem::transmute(vmax) };
let final_idxs: [u64; 2] = unsafe { std::mem::transmute(vidx) };
for k in 0..2 {
let idx = final_idxs[k] as usize;
if idx < n && final_vals[k] > best_val {
best_val = final_vals[k];
best_idx = idx;
}
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val {
best_val = val;
best_idx = j;
}
}
best_idx
}
#[cfg(target_arch = "aarch64")]
pub fn argmax_f64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_f64_neon(_: &[f64]) -> usize { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_f64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_f64_sse(a: &[f64]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
let mut vmax = _mm_loadu_pd(a.as_ptr());
let mut vidx = _mm_set_epi64x(1, 0);
i += 2;
while i + 2 <= n {
let va = _mm_loadu_pd(a.as_ptr().add(i));
let cmp = _mm_cmpgt_pd(va, vmax);
let local_idx = _mm_set_epi64x((i + 1) as i64, i as i64);
vmax = _mm_max_pd(vmax, va);
vidx = _mm_blendv_pd(vidx, local_idx, cmp);
i += 2;
}
let vals: [f64; 2] = std::mem::transmute(vmax);
let idxs: [i64; 2] = std::mem::transmute(vidx);
let mut best_idx = if vals[0] >= vals[1] { idxs[0] as usize } else { idxs[1] as usize };
let mut best_val = if vals[0] >= vals[1] { vals[0] } else { vals[1] };
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_f64_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "SSE4_1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_f64_sse(_: &[f64]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_f64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_f64_avx2(a: &[f64]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
let mut vmax = _mm256_loadu_pd(a.as_ptr());
let mut vidx = _mm256_set_epi64x(3, 2, 1, 0);
i += 4;
while i + 4 <= n {
let va = _mm256_loadu_pd(a.as_ptr().add(i));
let cmp = _mm256_cmp_pd(va, vmax, _CMP_GT_OS);
let local_idx = _mm256_set_epi64x((i + 3) as i64, (i + 2) as i64, (i + 1) as i64, i as i64);
vmax = _mm256_max_pd(vmax, va);
vidx = _mm256_blendv_pd(vidx, local_idx, cmp);
i += 4;
}
let vals: [f64; 4] = std::mem::transmute(vmax);
let idxs: [i64; 4] = std::mem::transmute(vidx);
let mut best_idx = idxs[0] as usize;
let mut best_val = vals[0];
for k in 1..4 {
if vals[k] > best_val { best_val = vals[k]; best_idx = idxs[k] as usize; }
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_f64_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_f64_avx2(_: &[f64]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_f64_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn argmax_f32_neon(a: &[f32]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::aarch64::*;
let mut i = 0;
let mut vmax = vld1q_f32(a.as_ptr());
let mut vidx: uint32x4_t = std::mem::transmute::<[u32; 4], _>([0, 1, 2, 3]);
i += 4;
while i + 4 <= n {
let va = vld1q_f32(a.as_ptr().add(i));
let vcmp = vcgtq_f32(va, vmax);
vmax = vmaxq_f32(vmax, va);
let local_idx: uint32x4_t = std::mem::transmute::<[u32; 4], _>([i as u32, (i + 1) as u32, (i + 2) as u32, (i + 3) as u32]);
vidx = vbslq_u32(vcmp, local_idx, vidx);
i += 4;
}
let final_vals: [f32; 4] = std::mem::transmute(vmax);
let final_idxs: [u32; 4] = std::mem::transmute(vidx);
let mut best_idx = final_idxs[0] as usize;
let mut best_val = final_vals[0];
for k in 1..4 {
let idx_k = final_idxs[k] as usize;
if idx_k < n && final_vals[k] > best_val {
best_val = final_vals[k];
best_idx = idx_k;
}
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "aarch64")]
pub fn argmax_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_f32_neon(_: &[f32]) -> usize { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_f32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_f32_sse(a: &[f32]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
let mut vmax = _mm_loadu_ps(a.as_ptr());
let mut vidx = _mm_set_epi32(3, 2, 1, 0);
i += 4;
while i + 4 <= n {
let va = _mm_loadu_ps(a.as_ptr().add(i));
let cmp = _mm_cmpgt_ps(va, vmax);
let local_idx = _mm_set_epi32((i + 3) as i32, (i + 2) as i32, (i + 1) as i32, i as i32);
vmax = _mm_max_ps(vmax, va);
vidx = _mm_blendv_ps(vidx, local_idx, cmp);
i += 4;
}
let vals: [f32; 4] = std::mem::transmute(vmax);
let idxs: [i32; 4] = std::mem::transmute(vidx);
let mut best_idx = idxs[0] as usize;
let mut best_val = vals[0];
for k in 1..4 {
if vals[k] > best_val { best_val = vals[k]; best_idx = idxs[k] as usize; }
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_f32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "SSE4_1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_f32_sse(_: &[f32]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_f32_avx2(a: &[f32]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
let mut vmax = _mm256_loadu_ps(a.as_ptr());
let mut vidx = _mm256_set_epi32(7, 6, 5, 4, 3, 2, 1, 0);
i += 8;
while i + 8 <= n {
let va = _mm256_loadu_ps(a.as_ptr().add(i));
let cmp = _mm256_cmp_ps(va, vmax, _CMP_GT_OS);
let local_idx = _mm256_set_epi32((i + 7) as i32, (i + 6) as i32, (i + 5) as i32, (i + 4) as i32, (i + 3) as i32, (i + 2) as i32, (i + 1) as i32, i as i32);
vmax = _mm256_max_ps(vmax, va);
vidx = _mm256_blendv_ps(vidx, local_idx, cmp);
i += 8;
}
let vals: [f32; 8] = std::mem::transmute(vmax);
let idxs: [i32; 8] = std::mem::transmute(vidx);
let mut best_idx = idxs[0] as usize;
let mut best_val = vals[0];
for k in 1..8 {
if vals[k] > best_val { best_val = vals[k]; best_idx = idxs[k] as usize; }
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_f32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_f32_avx2(_: &[f32]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn softmax_f32_scalar(input: &[f32], output: &mut [f32]) {
let max = input.iter().copied().fold(f32::NEG_INFINITY, f32::max);
let mut sum = 0.0;
for i in 0..input.len() {
let e = (input[i] - max).exp();
output[i] = e;
sum += e;
}
let inv = 1.0 / sum;
for v in output.iter_mut() {
*v *= inv;
}
}
pub fn softmax_f32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "x86_64")]
pub fn softmax_f32_sse(input: &[f32], output: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = input.len().min(output.len());
let mut i = 0;
let mut vmax = unsafe { _mm_set1_ps(f32::NEG_INFINITY) };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= n {
let v = _mm_loadu_ps(input.as_ptr().add(i));
vmax = _mm_max_ps(vmax, v);
i += 4;
}
}
}
let max_arr: [f32; 4] = unsafe { std::mem::transmute::<_, [f32; 4]>(vmax) };
let mut max = max_arr[0].max(max_arr[1]).max(max_arr[2]).max(max_arr[3]);
for &v in &input[i..n] {
if v > max { max = v; }
}
let mut sum = 0.0;
let mut i = 0;
let mut vacc = unsafe { _mm_setzero_ps() };
unsafe {
if is_x86_feature_detected!("sse2") {
let vmax = _mm_set1_ps(max);
while i + 4 <= n {
let v = _mm_loadu_ps(input.as_ptr().add(i));
let e = _mm_sub_ps(v, vmax);
let ea: [f32; 4] = std::mem::transmute::<_, [f32; 4]>(e);
let e0 = ea[0].exp();
let e1 = ea[1].exp();
let e2 = ea[2].exp();
let e3 = ea[3].exp();
_mm_storeu_ps(output.as_mut_ptr().add(i), _mm_set_ps(e3, e2, e1, e0));
vacc = _mm_add_ps(vacc, _mm_set_ps(e3, e2, e1, e0));
i += 4;
}
}
}
let sum_arr: [f32; 4] = unsafe { std::mem::transmute::<_, [f32; 4]>(vacc) };
sum = sum_arr[0] + sum_arr[1] + sum_arr[2] + sum_arr[3];
for j in i..n {
let e = (input[j] - max).exp();
output[j] = e;
sum += e;
}
let inv = 1.0 / sum;
let mut i = 0;
unsafe {
if is_x86_feature_detected!("sse2") {
let vinv = _mm_set1_ps(inv);
while i + 4 <= n {
let v = _mm_loadu_ps(output.as_ptr().add(i));
_mm_storeu_ps(output.as_mut_ptr().add(i), _mm_mul_ps(v, vinv));
i += 4;
}
}
}
for v in output[i..n].iter_mut() {
*v *= inv;
}
}
#[cfg(target_arch = "x86_64")]
pub fn softmax_f32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn softmax_f32_sse(_: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn softmax_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn softmax_f32_avx2(input: &[f32], output: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = input.len().min(output.len());
let mut i = 0;
let mut vmax = unsafe { _mm256_set1_ps(f32::NEG_INFINITY) };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= n {
let v = _mm256_loadu_ps(input.as_ptr().add(i));
vmax = _mm256_max_ps(vmax, v);
i += 8;
}
}
}
let max_arr: [f32; 8] = unsafe { std::mem::transmute::<_, [f32; 8]>(vmax) };
let mut max = max_arr.iter().copied().fold(f32::NEG_INFINITY, f32::max);
for &v in &input[i..n] {
if v > max { max = v; }
}
let mut i = 0;
let mut vacc = unsafe { _mm256_setzero_ps() };
unsafe {
if is_x86_feature_detected!("avx2") {
let vmax = _mm256_set1_ps(max);
while i + 8 <= n {
let v = _mm256_loadu_ps(input.as_ptr().add(i));
let e = _mm256_sub_ps(v, vmax);
let ea: [f32; 8] = std::mem::transmute::<_, [f32; 8]>(e);
let e0 = ea[0].exp();
let e1 = ea[1].exp();
let e2 = ea[2].exp();
let e3 = ea[3].exp();
let e4 = ea[4].exp();
let e5 = ea[5].exp();
let e6 = ea[6].exp();
let e7 = ea[7].exp();
_mm256_storeu_ps(output.as_mut_ptr().add(i), _mm256_set_ps(e7, e6, e5, e4, e3, e2, e1, e0));
vacc = _mm256_add_ps(vacc, _mm256_set_ps(e7, e6, e5, e4, e3, e2, e1, e0));
i += 8;
}
}
}
let sum_arr: [f32; 8] = unsafe { std::mem::transmute::<_, [f32; 8]>(vacc) };
let mut sum = sum_arr.iter().sum::<f32>();
for j in i..n {
let e = (input[j] - max).exp();
output[j] = e;
sum += e;
}
let inv = 1.0 / sum;
let mut i = 0;
unsafe {
if is_x86_feature_detected!("avx2") {
let vinv = _mm256_set1_ps(inv);
while i + 8 <= n {
let v = _mm256_loadu_ps(output.as_ptr().add(i));
_mm256_storeu_ps(output.as_mut_ptr().add(i), _mm256_mul_ps(v, vinv));
i += 8;
}
}
}
for v in output[i..n].iter_mut() {
*v *= inv;
}
}
#[cfg(target_arch = "x86_64")]
pub fn softmax_f32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn softmax_f32_avx2(_: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn softmax_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn softmax_f32_neon(input: &[f32], output: &mut [f32]) {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = input.len().min(output.len());
let mut i = 0;
let mut vmax = unsafe { vdupq_n_f32(f32::NEG_INFINITY) };
unsafe {
while i + 4 <= n {
let v = vld1q_f32(input.as_ptr().add(i));
vmax = vmaxq_f32(vmax, v);
i += 4;
}
}
let max_arr: [f32; 4] = unsafe { std::mem::transmute::<_, [f32; 4]>(vmax) };
let mut max = max_arr[0].max(max_arr[1]).max(max_arr[2]).max(max_arr[3]);
for &v in &input[i..n] {
if v > max { max = v; }
}
let mut i = 0;
let mut vacc = unsafe { vdupq_n_f32(0.0) };
unsafe {
let vmax = vdupq_n_f32(max);
while i + 4 <= n {
let v = vld1q_f32(input.as_ptr().add(i));
let e = vsubq_f32(v, vmax);
let ea: [f32; 4] = std::mem::transmute::<_, [f32; 4]>(e);
let e0 = ea[0].exp();
let e1 = ea[1].exp();
let e2 = ea[2].exp();
let e3 = ea[3].exp();
let ve = { let t: [f32; 4] = [e0, e1, e2, e3]; std::mem::transmute::<[f32; 4], float32x4_t>(t) };
vst1q_f32(output.as_mut_ptr().add(i), ve);
vacc = vaddq_f32(vacc, ve);
i += 4;
}
}
let sum_arr: [f32; 4] = unsafe { std::mem::transmute::<_, [f32; 4]>(vacc) };
let mut sum = sum_arr[0] + sum_arr[1] + sum_arr[2] + sum_arr[3];
for j in i..n {
let e = (input[j] - max).exp();
output[j] = e;
sum += e;
}
let inv = 1.0 / sum;
let mut i = 0;
unsafe {
let vinv = vdupq_n_f32(inv);
while i + 4 <= n {
let v = vld1q_f32(output.as_ptr().add(i));
vst1q_f32(output.as_mut_ptr().add(i), vmulq_f32(v, vinv));
i += 4;
}
}
for v in output[i..n].iter_mut() {
*v *= inv;
}
}
#[cfg(target_arch = "aarch64")]
pub fn softmax_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn softmax_f32_neon(_: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn softmax_f32_neon_supported(_: &HardwareDNA) -> bool { false }
pub fn matmul_f32_scalar(a: &[f32], b: &[f32], c: &mut [f32], n: usize) {
for i in 0..n {
for k in 0..n {
let aik = a[i * n + k];
let row_b = k * n;
let row_c = i * n;
for j in 0..n {
c[row_c + j] += aik * b[row_b + j];
}
}
}
}
pub fn matmul_f32_supported(_: &HardwareDNA) -> bool { true }
pub fn matmul_f32_tiled(a: &[f32], b: &[f32], c: &mut [f32], n: usize) {
const T: usize = 64;
for i in (0..n).step_by(T) {
let imax = (i + T).min(n);
for k in (0..n).step_by(T) {
let kmax = (k + T).min(n);
for j in (0..n).step_by(T) {
let jmax = (j + T).min(n);
for ii in i..imax {
for kk in k..kmax {
let aik = a[ii * n + kk];
let row_b = kk * n;
let row_c = ii * n;
for jj in j..jmax {
c[row_c + jj] += aik * b[row_b + jj];
}
}
}
}
}
}
}
pub fn matmul_f32_tiled_supported(_: &HardwareDNA) -> bool { true }
use himada_core::profile;
use std::sync::OnceLock;
fn optimal_tile_size_n_f32() -> usize {
static TILE: OnceLock<usize> = OnceLock::new();
*TILE.get_or_init(|| {
let dna = profile::load_or_collect();
let l1 = dna.caches.l1_data_size.max(16384);
let elems = (l1 / (3 * 4)) as usize;
let mut t = 1;
while (t << 1) <= elems {
t <<= 1;
}
t.clamp(8, 256)
})
}
pub fn matmul_f32_cache_tiled(a: &[f32], b: &[f32], c: &mut [f32], n: usize) {
let t = optimal_tile_size_n_f32();
for i in (0..n).step_by(t) {
let imax = (i + t).min(n);
for k in (0..n).step_by(t) {
let kmax = (k + t).min(n);
for j in (0..n).step_by(t) {
let jmax = (j + t).min(n);
for ii in i..imax {
for kk in k..kmax {
let aik = a[ii * n + kk];
let row_b = kk * n;
let row_c = ii * n;
for jj in j..jmax {
c[row_c + jj] += aik * b[row_b + jj];
}
}
}
}
}
}
}
pub fn matmul_f32_cache_tiled_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "x86_64")]
pub fn matmul_f32_sse(a: &[f32], b: &[f32], c: &mut [f32], n: usize) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
unsafe {
for i in 0..n {
for k in 0..n {
let aik = a[i * n + k];
let row_b = k * n;
let row_c = i * n;
let mut j = 0;
if is_x86_feature_detected!("sse2") {
let vaik = _mm_set1_ps(aik);
while j + 4 <= n {
let vb = _mm_loadu_ps(b.as_ptr().add(row_b + j));
let vc = _mm_loadu_ps(c.as_ptr().add(row_c + j));
let vm = _mm_mul_ps(vaik, vb);
_mm_storeu_ps(c.as_mut_ptr().add(row_c + j), _mm_add_ps(vc, vm));
j += 4;
}
}
for jj in j..n {
c[row_c + jj] += aik * b[row_b + jj];
}
}
}
}
}
#[cfg(target_arch = "x86_64")]
pub fn matmul_f32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn matmul_f32_sse(_: &[f32], _: &[f32], _: &mut [f32], _: usize) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn matmul_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn matmul_f32_avx2(a: &[f32], b: &[f32], c: &mut [f32], n: usize) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
unsafe {
for i in 0..n {
for k in 0..n {
let aik = a[i * n + k];
let row_b = k * n;
let row_c = i * n;
let mut j = 0;
if is_x86_feature_detected!("avx2") {
let vaik = _mm256_set1_ps(aik);
while j + 8 <= n {
let vb = _mm256_loadu_ps(b.as_ptr().add(row_b + j));
let vc = _mm256_loadu_ps(c.as_ptr().add(row_c + j));
let vm = _mm256_mul_ps(vaik, vb);
_mm256_storeu_ps(c.as_mut_ptr().add(row_c + j), _mm256_add_ps(vc, vm));
j += 8;
}
}
for jj in j..n {
c[row_c + jj] += aik * b[row_b + jj];
}
}
}
}
}
#[cfg(target_arch = "x86_64")]
pub fn matmul_f32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn matmul_f32_avx2(_: &[f32], _: &[f32], _: &mut [f32], _: usize) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn matmul_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn matmul_f32_neon(a: &[f32], b: &[f32], c: &mut [f32], n: usize) {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
unsafe {
for i in 0..n {
for k in 0..n {
let aik = a[i * n + k];
let row_b = k * n;
let row_c = i * n;
let mut j = 0;
if n >= 4 {
let vaik = vdupq_n_f32(aik);
while j + 4 <= n {
let vb = vld1q_f32(b.as_ptr().add(row_b + j));
let vc = vld1q_f32(c.as_ptr().add(row_c + j));
let vm = vmulq_f32(vaik, vb);
vst1q_f32(c.as_mut_ptr().add(row_c + j), vaddq_f32(vc, vm));
j += 4;
}
}
for jj in j..n {
c[row_c + jj] += aik * b[row_b + jj];
}
}
}
}
}
#[cfg(target_arch = "aarch64")]
pub fn matmul_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn matmul_f32_neon(_: &[f32], _: &[f32], _: &mut [f32], _: usize) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn matmul_f32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_f32_neon(a: &[f32], b: &[f32], c: &mut [f32]) {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
while i + 4 <= len {
let va = vld1q_f32(a.as_ptr().add(i));
let vb = vld1q_f32(b.as_ptr().add(i));
vst1q_f32(c.as_mut_ptr().add(i), vmulq_f32(va, vb));
i += 4;
}
}
for j in i..len {
c[j] = a[j] * b[j];
}
}
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_f32_neon(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_f32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_f32_sse(a: &[f32], b: &[f32], c: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= len {
let va = _mm_loadu_ps(a.as_ptr().add(i));
let vb = _mm_loadu_ps(b.as_ptr().add(i));
_mm_storeu_ps(c.as_mut_ptr().add(i), _mm_mul_ps(va, vb));
i += 4;
}
}
}
for j in i..len {
c[j] = a[j] * b[j];
}
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_f32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_f32_sse(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_f32_avx2(a: &[f32], b: &[f32], c: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= len {
let va = _mm256_loadu_ps(a.as_ptr().add(i));
let vb = _mm256_loadu_ps(b.as_ptr().add(i));
_mm256_storeu_ps(c.as_mut_ptr().add(i), _mm256_mul_ps(va, vb));
i += 8;
}
}
}
for j in i..len {
c[j] = a[j] * b[j];
}
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_f32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_f32_avx2(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn sub_f32_neon(a: &[f32], b: &[f32], c: &mut [f32]) {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
while i + 4 <= len {
let va = vld1q_f32(a.as_ptr().add(i));
let vb = vld1q_f32(b.as_ptr().add(i));
vst1q_f32(c.as_mut_ptr().add(i), vsubq_f32(va, vb));
i += 4;
}
}
for j in i..len {
c[j] = a[j] - b[j];
}
}
#[cfg(target_arch = "aarch64")]
pub fn sub_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn sub_f32_neon(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn sub_f32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn sub_f32_sse(a: &[f32], b: &[f32], c: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= len {
let va = _mm_loadu_ps(a.as_ptr().add(i));
let vb = _mm_loadu_ps(b.as_ptr().add(i));
_mm_storeu_ps(c.as_mut_ptr().add(i), _mm_sub_ps(va, vb));
i += 4;
}
}
}
for j in i..len {
c[j] = a[j] - b[j];
}
}
#[cfg(target_arch = "x86_64")]
pub fn sub_f32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_f32_sse(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn sub_f32_avx2(a: &[f32], b: &[f32], c: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= len {
let va = _mm256_loadu_ps(a.as_ptr().add(i));
let vb = _mm256_loadu_ps(b.as_ptr().add(i));
_mm256_storeu_ps(c.as_mut_ptr().add(i), _mm256_sub_ps(va, vb));
i += 8;
}
}
}
for j in i..len {
c[j] = a[j] - b[j];
}
}
#[cfg(target_arch = "x86_64")]
pub fn sub_f32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_f32_avx2(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn mul_f32_neon(a: &[f32], b: &[f32], c: &mut [f32]) {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
while i + 4 <= len {
let va = vld1q_f32(a.as_ptr().add(i));
let vb = vld1q_f32(b.as_ptr().add(i));
vst1q_f32(c.as_mut_ptr().add(i), vmulq_f32(va, vb));
i += 4;
}
}
for j in i..len {
c[j] = a[j] * b[j];
}
}
#[cfg(target_arch = "aarch64")]
pub fn mul_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn mul_f32_neon(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn mul_f32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn mul_f32_sse(a: &[f32], b: &[f32], c: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= len {
let va = _mm_loadu_ps(a.as_ptr().add(i));
let vb = _mm_loadu_ps(b.as_ptr().add(i));
_mm_storeu_ps(c.as_mut_ptr().add(i), _mm_mul_ps(va, vb));
i += 4;
}
}
}
for j in i..len {
c[j] = a[j] * b[j];
}
}
#[cfg(target_arch = "x86_64")]
pub fn mul_f32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_f32_sse(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn mul_f32_avx2(a: &[f32], b: &[f32], c: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= len {
let va = _mm256_loadu_ps(a.as_ptr().add(i));
let vb = _mm256_loadu_ps(b.as_ptr().add(i));
_mm256_storeu_ps(c.as_mut_ptr().add(i), _mm256_mul_ps(va, vb));
i += 8;
}
}
}
for j in i..len {
c[j] = a[j] * b[j];
}
}
#[cfg(target_arch = "x86_64")]
pub fn mul_f32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_f32_avx2(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn cosine_similarity_f32_neon(a: &[f32], b: &[f32]) -> f32 {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let len = a.len().min(b.len());
let mut i = 0;
let mut acc_dot = unsafe { vdupq_n_f32(0.0) };
let mut acc_na = unsafe { vdupq_n_f32(0.0) };
let mut acc_nb = unsafe { vdupq_n_f32(0.0) };
unsafe {
while i + 4 <= len {
let va = vld1q_f32(a.as_ptr().add(i));
let vb = vld1q_f32(b.as_ptr().add(i));
acc_dot = vaddq_f32(acc_dot, vmulq_f32(va, vb));
acc_na = vaddq_f32(acc_na, vmulq_f32(va, va));
acc_nb = vaddq_f32(acc_nb, vmulq_f32(vb, vb));
i += 4;
}
}
let td: [f32; 4] = unsafe { std::mem::transmute::<_, [f32; 4]>(acc_dot) };
let tn: [f32; 4] = unsafe { std::mem::transmute::<_, [f32; 4]>(acc_na) };
let tnb: [f32; 4] = unsafe { std::mem::transmute::<_, [f32; 4]>(acc_nb) };
let mut dot = td[0] + td[1] + td[2] + td[3];
let mut na = tn[0] + tn[1] + tn[2] + tn[3];
let mut nb = tnb[0] + tnb[1] + tnb[2] + tnb[3];
for j in i..len {
dot += a[j] * b[j];
na += a[j] * a[j];
nb += b[j] * b[j];
}
dot / (na.sqrt() * nb.sqrt())
}
#[cfg(target_arch = "aarch64")]
pub fn cosine_similarity_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn cosine_similarity_f32_neon(_: &[f32], _: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn cosine_similarity_f32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn cosine_similarity_f32_sse(a: &[f32], b: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len());
let mut i = 0;
let mut acc_dot = unsafe { _mm_setzero_ps() };
let mut acc_na = unsafe { _mm_setzero_ps() };
let mut acc_nb = unsafe { _mm_setzero_ps() };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= len {
let va = _mm_loadu_ps(a.as_ptr().add(i));
let vb = _mm_loadu_ps(b.as_ptr().add(i));
acc_dot = _mm_add_ps(acc_dot, _mm_mul_ps(va, vb));
acc_na = _mm_add_ps(acc_na, _mm_mul_ps(va, va));
acc_nb = _mm_add_ps(acc_nb, _mm_mul_ps(vb, vb));
i += 4;
}
}
}
let td: [f32; 4] = unsafe { std::mem::transmute::<_, [f32; 4]>(acc_dot) };
let tn: [f32; 4] = unsafe { std::mem::transmute::<_, [f32; 4]>(acc_na) };
let tnb: [f32; 4] = unsafe { std::mem::transmute::<_, [f32; 4]>(acc_nb) };
let mut dot = td[0] + td[1] + td[2] + td[3];
let mut na = tn[0] + tn[1] + tn[2] + tn[3];
let mut nb = tnb[0] + tnb[1] + tnb[2] + tnb[3];
for j in i..len {
dot += a[j] * b[j];
na += a[j] * a[j];
nb += b[j] * b[j];
}
dot / (na.sqrt() * nb.sqrt())
}
#[cfg(target_arch = "x86_64")]
pub fn cosine_similarity_f32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn cosine_similarity_f32_sse(_: &[f32], _: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn cosine_similarity_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn cosine_similarity_f32_avx2(a: &[f32], b: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len());
let mut i = 0;
let mut acc_dot = unsafe { _mm256_setzero_ps() };
let mut acc_na = unsafe { _mm256_setzero_ps() };
let mut acc_nb = unsafe { _mm256_setzero_ps() };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= len {
let va = _mm256_loadu_ps(a.as_ptr().add(i));
let vb = _mm256_loadu_ps(b.as_ptr().add(i));
acc_dot = _mm256_add_ps(acc_dot, _mm256_mul_ps(va, vb));
acc_na = _mm256_add_ps(acc_na, _mm256_mul_ps(va, va));
acc_nb = _mm256_add_ps(acc_nb, _mm256_mul_ps(vb, vb));
i += 8;
}
}
}
let td: [f32; 8] = unsafe { std::mem::transmute::<_, [f32; 8]>(acc_dot) };
let tn: [f32; 8] = unsafe { std::mem::transmute::<_, [f32; 8]>(acc_na) };
let tnb: [f32; 8] = unsafe { std::mem::transmute::<_, [f32; 8]>(acc_nb) };
let mut dot: f32 = td.iter().sum();
let mut na: f32 = tn.iter().sum();
let mut nb: f32 = tnb.iter().sum();
for j in i..len {
dot += a[j] * b[j];
na += a[j] * a[j];
nb += b[j] * b[j];
}
dot / (na.sqrt() * nb.sqrt())
}
#[cfg(target_arch = "x86_64")]
pub fn cosine_similarity_f32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn cosine_similarity_f32_avx2(_: &[f32], _: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn cosine_similarity_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn euclidean_distance_f32_scalar(a: &[f32], b: &[f32]) -> f32 {
let n = a.len().min(b.len());
let mut sum = 0.0;
for i in 0..n { let d = a[i] - b[i]; sum += d * d; }
sum.sqrt()
}
pub fn euclidean_distance_f32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn euclidean_distance_f32_neon(a: &[f32], b: &[f32]) -> f32 {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc = unsafe { vdupq_n_f32(0.0) };
unsafe {
while i + 4 <= len {
let va = vld1q_f32(a.as_ptr().add(i));
let vb = vld1q_f32(b.as_ptr().add(i));
let d = vsubq_f32(va, vb);
vacc = vaddq_f32(vacc, vmulq_f32(d, d));
i += 4;
}
}
let tmp: [f32; 4] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp[0] + tmp[1] + tmp[2] + tmp[3];
for j in i..len { let d = a[j] - b[j]; sum += d * d; }
sum.sqrt()
}
#[cfg(target_arch = "aarch64")]
pub fn euclidean_distance_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn euclidean_distance_f32_neon(_: &[f32], _: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn euclidean_distance_f32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn euclidean_distance_f32_sse(a: &[f32], b: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc = unsafe { _mm_setzero_ps() };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= len {
let va = _mm_loadu_ps(a.as_ptr().add(i));
let vb = _mm_loadu_ps(b.as_ptr().add(i));
let d = _mm_sub_ps(va, vb);
vacc = _mm_add_ps(vacc, _mm_mul_ps(d, d));
i += 4;
}
}
}
let tmp: [f32; 4] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp[0] + tmp[1] + tmp[2] + tmp[3];
for j in i..len { let d = a[j] - b[j]; sum += d * d; }
sum.sqrt()
}
#[cfg(target_arch = "x86_64")]
pub fn euclidean_distance_f32_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn euclidean_distance_f32_sse(_: &[f32], _: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn euclidean_distance_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn euclidean_distance_f32_avx2(a: &[f32], b: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc = unsafe { _mm256_setzero_ps() };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= len {
let va = _mm256_loadu_ps(a.as_ptr().add(i));
let vb = _mm256_loadu_ps(b.as_ptr().add(i));
let d = _mm256_sub_ps(va, vb);
vacc = _mm256_add_ps(vacc, _mm256_mul_ps(d, d));
i += 8;
}
}
}
let tmp: [f32; 8] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp.iter().sum::<f32>();
for j in i..len { let d = a[j] - b[j]; sum += d * d; }
sum.sqrt()
}
#[cfg(target_arch = "x86_64")]
pub fn euclidean_distance_f32_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn euclidean_distance_f32_avx2(_: &[f32], _: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn euclidean_distance_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn add_f32_scalar(a: &[f32], b: &[f32], out: &mut [f32]) {
let n = a.len().min(b.len()).min(out.len());
for i in 0..n { out[i] = a[i] + b[i]; }
}
pub fn add_f32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "x86_64")]
pub fn add_f32_sse(a: &[f32], b: &[f32], out: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(b.len()).min(out.len());
let mut i = 0;
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= n {
let va = _mm_loadu_ps(a.as_ptr().add(i));
let vb = _mm_loadu_ps(b.as_ptr().add(i));
_mm_storeu_ps(out.as_mut_ptr().add(i), _mm_add_ps(va, vb));
i += 4;
}
}
}
for j in i..n { out[j] = a[j] + b[j]; }
}
#[cfg(target_arch = "x86_64")]
pub fn add_f32_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn add_f32_sse(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn add_f32_avx2(a: &[f32], b: &[f32], out: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(b.len()).min(out.len());
let mut i = 0;
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= n {
let va = _mm256_loadu_ps(a.as_ptr().add(i));
let vb = _mm256_loadu_ps(b.as_ptr().add(i));
_mm256_storeu_ps(out.as_mut_ptr().add(i), _mm256_add_ps(va, vb));
i += 8;
}
}
}
for j in i..n { out[j] = a[j] + b[j]; }
}
#[cfg(target_arch = "x86_64")]
pub fn add_f32_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn add_f32_avx2(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn add_f32_neon(a: &[f32], b: &[f32], out: &mut [f32]) {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len().min(b.len()).min(out.len());
let mut i = 0;
unsafe {
while i + 4 <= n {
let va = vld1q_f32(a.as_ptr().add(i));
let vb = vld1q_f32(b.as_ptr().add(i));
vst1q_f32(out.as_mut_ptr().add(i), vaddq_f32(va, vb));
i += 4;
}
}
for j in i..n { out[j] = a[j] + b[j]; }
}
#[cfg(target_arch = "aarch64")]
pub fn add_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn add_f32_neon(_: &[f32], _: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn add_f32_neon_supported(_: &HardwareDNA) -> bool { false }
pub fn negate_f32_scalar(a: &[f32], out: &mut [f32]) {
let n = a.len().min(out.len());
for i in 0..n { out[i] = -a[i]; }
}
pub fn negate_f32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "x86_64")]
pub fn negate_f32_sse(a: &[f32], out: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(out.len());
let mut i = 0;
let sign_mask = unsafe { _mm_set1_ps(-0.0f32) };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= n {
let va = _mm_loadu_ps(a.as_ptr().add(i));
_mm_storeu_ps(out.as_mut_ptr().add(i), _mm_xor_ps(va, sign_mask));
i += 4;
}
}
}
for j in i..n { out[j] = -a[j]; }
}
#[cfg(target_arch = "x86_64")]
pub fn negate_f32_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn negate_f32_sse(_: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn negate_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn negate_f32_avx2(a: &[f32], out: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(out.len());
let mut i = 0;
let sign_mask = unsafe { _mm256_set1_ps(-0.0f32) };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= n {
let va = _mm256_loadu_ps(a.as_ptr().add(i));
_mm256_storeu_ps(out.as_mut_ptr().add(i), _mm256_xor_ps(va, sign_mask));
i += 8;
}
}
}
for j in i..n { out[j] = -a[j]; }
}
#[cfg(target_arch = "x86_64")]
pub fn negate_f32_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn negate_f32_avx2(_: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn negate_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn negate_f32_neon(a: &[f32], out: &mut [f32]) {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len().min(out.len());
let mut i = 0;
unsafe {
while i + 4 <= n {
let va = vld1q_f32(a.as_ptr().add(i));
let vneg = vnegq_f32(va);
vst1q_f32(out.as_mut_ptr().add(i), vneg);
i += 4;
}
}
for j in i..n { out[j] = -a[j]; }
}
#[cfg(target_arch = "aarch64")]
pub fn negate_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn negate_f32_neon(_: &[f32], _: &mut [f32]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn negate_f32_neon_supported(_: &HardwareDNA) -> bool { false }
pub fn clamp_f32_scalar(a: &[f32], lo: f32, hi: f32, out: &mut [f32]) {
let n = a.len().min(out.len());
for i in 0..n { out[i] = a[i].clamp(lo, hi); }
}
pub fn clamp_f32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "x86_64")]
pub fn clamp_f32_sse(a: &[f32], lo: f32, hi: f32, out: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(out.len());
let mut i = 0;
let vlo = unsafe { _mm_set1_ps(lo) };
let vhi = unsafe { _mm_set1_ps(hi) };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= n {
let va = _mm_loadu_ps(a.as_ptr().add(i));
let clamped = _mm_min_ps(_mm_max_ps(va, vlo), vhi);
_mm_storeu_ps(out.as_mut_ptr().add(i), clamped);
i += 4;
}
}
}
for j in i..n { out[j] = a[j].clamp(lo, hi); }
}
#[cfg(target_arch = "x86_64")]
pub fn clamp_f32_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn clamp_f32_sse(_: &[f32], _: f32, _: f32, _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn clamp_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn clamp_f32_avx2(a: &[f32], lo: f32, hi: f32, out: &mut [f32]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(out.len());
let mut i = 0;
let vlo = unsafe { _mm256_set1_ps(lo) };
let vhi = unsafe { _mm256_set1_ps(hi) };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= n {
let va = _mm256_loadu_ps(a.as_ptr().add(i));
let clamped = _mm256_min_ps(_mm256_max_ps(va, vlo), vhi);
_mm256_storeu_ps(out.as_mut_ptr().add(i), clamped);
i += 8;
}
}
}
for j in i..n { out[j] = a[j].clamp(lo, hi); }
}
#[cfg(target_arch = "x86_64")]
pub fn clamp_f32_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn clamp_f32_avx2(_: &[f32], _: f32, _: f32, _: &mut [f32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn clamp_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn clamp_f32_neon(a: &[f32], lo: f32, hi: f32, out: &mut [f32]) {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len().min(out.len());
let mut i = 0;
let vlo = unsafe { vdupq_n_f32(lo) };
let vhi = unsafe { vdupq_n_f32(hi) };
unsafe {
while i + 4 <= n {
let va = vld1q_f32(a.as_ptr().add(i));
let clamped = vminq_f32(vmaxq_f32(va, vlo), vhi);
vst1q_f32(out.as_mut_ptr().add(i), clamped);
i += 4;
}
}
for j in i..n { out[j] = a[j].clamp(lo, hi); }
}
#[cfg(target_arch = "aarch64")]
pub fn clamp_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn clamp_f32_neon(_: &[f32], _: f32, _: f32, _: &mut [f32]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn clamp_f32_neon_supported(_: &HardwareDNA) -> bool { false }
pub fn cosine_similarity_f64_scalar(a: &[f64], b: &[f64]) -> f64 {
let n = a.len().min(b.len());
let mut dot = 0.0; let mut na = 0.0; let mut nb = 0.0;
for i in 0..n { dot += a[i] * b[i]; na += a[i] * a[i]; nb += b[i] * b[i]; }
let denom = na.sqrt() * nb.sqrt();
if denom == 0.0 { 0.0 } else { dot / denom }
}
pub fn cosine_similarity_f64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn cosine_similarity_f64_neon(a: &[f64], b: &[f64]) -> f64 {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let len = a.len().min(b.len());
let mut i = 0;
let mut acc_dot = unsafe { vdupq_n_f64(0.0) };
let mut acc_na = unsafe { vdupq_n_f64(0.0) };
let mut acc_nb = unsafe { vdupq_n_f64(0.0) };
unsafe {
while i + 2 <= len {
let va = vld1q_f64(a.as_ptr().add(i));
let vb = vld1q_f64(b.as_ptr().add(i));
acc_dot = vaddq_f64(acc_dot, vmulq_f64(va, vb));
acc_na = vaddq_f64(acc_na, vmulq_f64(va, va));
acc_nb = vaddq_f64(acc_nb, vmulq_f64(vb, vb));
i += 2;
}
}
let td: [f64; 2] = unsafe { std::mem::transmute(acc_dot) };
let tn: [f64; 2] = unsafe { std::mem::transmute(acc_na) };
let tnb: [f64; 2] = unsafe { std::mem::transmute(acc_nb) };
let mut dot = td[0] + td[1]; let mut na = tn[0] + tn[1]; let mut nb = tnb[0] + tnb[1];
for j in i..len { dot += a[j] * b[j]; na += a[j] * a[j]; nb += b[j] * b[j]; }
let denom = na.sqrt() * nb.sqrt(); if denom == 0.0 { 0.0 } else { dot / denom }
}
#[cfg(target_arch = "aarch64")]
pub fn cosine_similarity_f64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn cosine_similarity_f64_neon(_: &[f64], _: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn cosine_similarity_f64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn cosine_similarity_f64_sse(a: &[f64], b: &[f64]) -> f64 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len());
let mut i = 0;
let mut acc_dot = unsafe { _mm_setzero_pd() };
let mut acc_na = unsafe { _mm_setzero_pd() };
let mut acc_nb = unsafe { _mm_setzero_pd() };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 2 <= len {
let va = _mm_loadu_pd(a.as_ptr().add(i));
let vb = _mm_loadu_pd(b.as_ptr().add(i));
acc_dot = _mm_add_pd(acc_dot, _mm_mul_pd(va, vb));
acc_na = _mm_add_pd(acc_na, _mm_mul_pd(va, va));
acc_nb = _mm_add_pd(acc_nb, _mm_mul_pd(vb, vb));
i += 2;
}
}
}
let td: [f64; 2] = unsafe { std::mem::transmute(acc_dot) };
let tn: [f64; 2] = unsafe { std::mem::transmute(acc_na) };
let tnb: [f64; 2] = unsafe { std::mem::transmute(acc_nb) };
let mut dot = td[0] + td[1]; let mut na = tn[0] + tn[1]; let mut nb = tnb[0] + tnb[1];
for j in i..len { dot += a[j] * b[j]; na += a[j] * a[j]; nb += b[j] * b[j]; }
let denom = na.sqrt() * nb.sqrt(); if denom == 0.0 { 0.0 } else { dot / denom }
}
#[cfg(target_arch = "x86_64")]
pub fn cosine_similarity_f64_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn cosine_similarity_f64_sse(_: &[f64], _: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn cosine_similarity_f64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn cosine_similarity_f64_avx2(a: &[f64], b: &[f64]) -> f64 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len());
let mut i = 0;
let mut acc_dot = unsafe { _mm256_setzero_pd() };
let mut acc_na = unsafe { _mm256_setzero_pd() };
let mut acc_nb = unsafe { _mm256_setzero_pd() };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 4 <= len {
let va = _mm256_loadu_pd(a.as_ptr().add(i));
let vb = _mm256_loadu_pd(b.as_ptr().add(i));
acc_dot = _mm256_add_pd(acc_dot, _mm256_mul_pd(va, vb));
acc_na = _mm256_add_pd(acc_na, _mm256_mul_pd(va, va));
acc_nb = _mm256_add_pd(acc_nb, _mm256_mul_pd(vb, vb));
i += 4;
}
}
}
let td: [f64; 4] = unsafe { std::mem::transmute(acc_dot) };
let tn: [f64; 4] = unsafe { std::mem::transmute(acc_na) };
let tnb: [f64; 4] = unsafe { std::mem::transmute(acc_nb) };
let mut dot = td.iter().sum(); let mut na = tn.iter().sum(); let mut nb = tnb.iter().sum();
for j in i..len { dot += a[j] * b[j]; na += a[j] * a[j]; nb += b[j] * b[j]; }
let denom = na.sqrt() * nb.sqrt(); if denom == 0.0 { 0.0 } else { dot / denom }
}
#[cfg(target_arch = "x86_64")]
pub fn cosine_similarity_f64_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn cosine_similarity_f64_avx2(_: &[f64], _: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn cosine_similarity_f64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn cosine_similarity_f32_scalar(a: &[f32], b: &[f32]) -> f32 {
let n = a.len().min(b.len());
let mut dot = 0.0; let mut na = 0.0; let mut nb = 0.0;
for i in 0..n { dot += a[i] * b[i]; na += a[i] * a[i]; nb += b[i] * b[i]; }
let denom = na.sqrt() * nb.sqrt();
if denom == 0.0 { 0.0 } else { dot / denom }
}
pub fn cosine_similarity_f32_supported(_: &HardwareDNA) -> bool { true }
pub fn euclidean_distance_f64_scalar(a: &[f64], b: &[f64]) -> f64 {
let n = a.len().min(b.len()); let mut sum = 0.0;
for i in 0..n { let d = a[i] - b[i]; sum += d * d; }
sum.sqrt()
}
pub fn euclidean_distance_f64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn euclidean_distance_f64_neon(a: &[f64], b: &[f64]) -> f64 {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let len = a.len().min(b.len()); let mut i = 0;
let mut vacc = unsafe { vdupq_n_f64(0.0) };
unsafe {
while i + 2 <= len {
let va = vld1q_f64(a.as_ptr().add(i)); let vb = vld1q_f64(b.as_ptr().add(i));
let d = vsubq_f64(va, vb); vacc = vaddq_f64(vacc, vmulq_f64(d, d)); i += 2;
}
}
let tmp: [f64; 2] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp[0] + tmp[1];
for j in i..len { let d = a[j] - b[j]; sum += d * d; }
sum.sqrt()
}
#[cfg(target_arch = "aarch64")]
pub fn euclidean_distance_f64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn euclidean_distance_f64_neon(_: &[f64], _: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn euclidean_distance_f64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn euclidean_distance_f64_sse(a: &[f64], b: &[f64]) -> f64 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len()); let mut i = 0;
let mut vacc = unsafe { _mm_setzero_pd() };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 2 <= len {
let va = _mm_loadu_pd(a.as_ptr().add(i)); let vb = _mm_loadu_pd(b.as_ptr().add(i));
let d = _mm_sub_pd(va, vb); vacc = _mm_add_pd(vacc, _mm_mul_pd(d, d)); i += 2;
}
}
}
let tmp: [f64; 2] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp[0] + tmp[1];
for j in i..len { let d = a[j] - b[j]; sum += d * d; }
sum.sqrt()
}
#[cfg(target_arch = "x86_64")]
pub fn euclidean_distance_f64_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn euclidean_distance_f64_sse(_: &[f64], _: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn euclidean_distance_f64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn euclidean_distance_f64_avx2(a: &[f64], b: &[f64]) -> f64 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let len = a.len().min(b.len()); let mut i = 0;
let mut vacc = unsafe { _mm256_setzero_pd() };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 4 <= len {
let va = _mm256_loadu_pd(a.as_ptr().add(i)); let vb = _mm256_loadu_pd(b.as_ptr().add(i));
let d = _mm256_sub_pd(va, vb); vacc = _mm256_add_pd(vacc, _mm256_mul_pd(d, d)); i += 4;
}
}
}
let tmp: [f64; 4] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp.iter().sum::<f64>();
for j in i..len { let d = a[j] - b[j]; sum += d * d; }
sum.sqrt()
}
#[cfg(target_arch = "x86_64")]
pub fn euclidean_distance_f64_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn euclidean_distance_f64_avx2(_: &[f64], _: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn euclidean_distance_f64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn abs_max_f64_scalar(a: &[f64]) -> f64 {
let mut max = 0.0; for &v in a { let av = v.abs(); if av > max { max = av; } } max
}
pub fn abs_max_f64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn abs_max_f64_neon(a: &[f64]) -> f64 {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len(); if n == 0 { return 0.0; }
let mut i = 0; let mut vmax = unsafe { vdupq_n_f64(0.0) };
unsafe {
while i + 2 <= n {
let v = vld1q_f64(a.as_ptr().add(i)); let va = vabsq_f64(v);
vmax = vmaxq_f64(vmax, va); i += 2;
}
}
let arr: [f64; 2] = unsafe { std::mem::transmute(vmax) };
let mut max = arr[0].max(arr[1]);
for &v in &a[i..n] { let av = v.abs(); if av > max { max = av; } }
max
}
#[cfg(target_arch = "aarch64")]
pub fn abs_max_f64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_f64_neon(_: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_f64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_f64_sse(a: &[f64]) -> f64 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len(); if n == 0 { return 0.0; }
let mut i = 0; let mut vmax = unsafe { _mm_setzero_pd() };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 2 <= n {
let v = _mm_loadu_pd(a.as_ptr().add(i)); let va = _mm_andnot_pd(_mm_set1_pd(-0.0), v);
vmax = _mm_max_pd(vmax, va); i += 2;
}
}
}
let arr: [f64; 2] = unsafe { std::mem::transmute(vmax) };
let mut max = arr[0].max(arr[1]);
for &v in &a[i..n] { let av = v.abs(); if av > max { max = av; } }
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_f64_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_f64_sse(_: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_f64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_f64_avx2(a: &[f64]) -> f64 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len(); if n == 0 { return 0.0; }
let mut i = 0; let mut vmax = unsafe { _mm256_setzero_pd() };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 4 <= n {
let v = _mm256_loadu_pd(a.as_ptr().add(i)); let va = _mm256_andnot_pd(_mm256_set1_pd(-0.0), v);
vmax = _mm256_max_pd(vmax, va); i += 4;
}
}
}
let arr: [f64; 4] = unsafe { std::mem::transmute(vmax) };
let mut max = arr.iter().copied().fold(0.0_f64, f64::max);
for &v in &a[i..n] { let av = v.abs(); if av > max { max = av; } }
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_f64_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_f64_avx2(_: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_f64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn abs_max_f32_scalar(a: &[f32]) -> f32 {
let mut max = 0.0; for &v in a { let av = v.abs(); if av > max { max = av; } } max
}
pub fn abs_max_f32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn abs_max_f32_neon(a: &[f32]) -> f32 {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len(); if n == 0 { return 0.0; }
let mut i = 0; let mut vmax = unsafe { vdupq_n_f32(0.0) };
unsafe {
while i + 4 <= n {
let v = vld1q_f32(a.as_ptr().add(i)); let va = vabsq_f32(v);
vmax = vmaxq_f32(vmax, va); i += 4;
}
}
let arr: [f32; 4] = unsafe { std::mem::transmute(vmax) };
let mut max = arr[0].max(arr[1]).max(arr[2]).max(arr[3]);
for &v in &a[i..n] { let av = v.abs(); if av > max { max = av; } }
max
}
#[cfg(target_arch = "aarch64")]
pub fn abs_max_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_f32_neon(_: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_f32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_f32_sse(a: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len(); if n == 0 { return 0.0; }
let mut i = 0; let mut vmax = unsafe { _mm_setzero_ps() };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= n {
let v = _mm_loadu_ps(a.as_ptr().add(i)); let va = _mm_andnot_ps(_mm_set1_ps(-0.0), v);
vmax = _mm_max_ps(vmax, va); i += 4;
}
}
}
let arr: [f32; 4] = unsafe { std::mem::transmute(vmax) };
let mut max = arr[0].max(arr[1]).max(arr[2]).max(arr[3]);
for &v in &a[i..n] { let av = v.abs(); if av > max { max = av; } }
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_f32_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_f32_sse(_: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_f32_avx2(a: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len(); if n == 0 { return 0.0; }
let mut i = 0; let mut vmax = unsafe { _mm256_setzero_ps() };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= n {
let v = _mm256_loadu_ps(a.as_ptr().add(i)); let va = _mm256_andnot_ps(_mm256_set1_ps(-0.0), v);
vmax = _mm256_max_ps(vmax, va); i += 8;
}
}
}
let arr: [f32; 8] = unsafe { std::mem::transmute(vmax) };
let mut max = arr.iter().copied().fold(0.0_f32, f32::max);
for &v in &a[i..n] { let av = v.abs(); if av > max { max = av; } }
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_f32_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_f32_avx2(_: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn argmax_f64_scalar(a: &[f64]) -> usize {
let n = a.len(); if n == 0 { return 0; }
let mut idx = 0; let mut max = a[0];
for i in 1..n { if a[i] > max { max = a[i]; idx = i; } }
idx
}
pub fn argmax_f32_scalar(a: &[f32]) -> usize {
let n = a.len(); if n == 0 { return 0; }
let mut idx = 0; let mut max = a[0];
for i in 1..n { if a[i] > max { max = a[i]; idx = i; } }
idx
}
pub fn hadamard_product_f64_scalar(a: &[f64], b: &[f64], out: &mut [f64]) {
let n = a.len().min(b.len()).min(out.len());
for i in 0..n { out[i] = a[i] * b[i]; }
}
pub fn hadamard_product_f64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_f64_neon(a: &[f64], b: &[f64], out: &mut [f64]) {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len().min(b.len()).min(out.len()); let mut i = 0;
unsafe {
while i + 2 <= n {
let va = vld1q_f64(a.as_ptr().add(i)); let vb = vld1q_f64(b.as_ptr().add(i));
vst1q_f64(out.as_mut_ptr().add(i), vmulq_f64(va, vb)); i += 2;
}
}
for j in i..n { out[j] = a[j] * b[j]; }
}
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_f64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_f64_neon(_: &[f64], _: &[f64], _: &mut [f64]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_f64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_f64_sse(a: &[f64], b: &[f64], out: &mut [f64]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(b.len()).min(out.len()); let mut i = 0;
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 2 <= n {
let va = _mm_loadu_pd(a.as_ptr().add(i)); let vb = _mm_loadu_pd(b.as_ptr().add(i));
_mm_storeu_pd(out.as_mut_ptr().add(i), _mm_mul_pd(va, vb)); i += 2;
}
}
}
for j in i..n { out[j] = a[j] * b[j]; }
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_f64_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_f64_sse(_: &[f64], _: &[f64], _: &mut [f64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_f64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_f64_avx2(a: &[f64], b: &[f64], out: &mut [f64]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(b.len()).min(out.len()); let mut i = 0;
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 4 <= n {
let va = _mm256_loadu_pd(a.as_ptr().add(i)); let vb = _mm256_loadu_pd(b.as_ptr().add(i));
_mm256_storeu_pd(out.as_mut_ptr().add(i), _mm256_mul_pd(va, vb)); i += 4;
}
}
}
for j in i..n { out[j] = a[j] * b[j]; }
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_f64_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_f64_avx2(_: &[f64], _: &[f64], _: &mut [f64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_f64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn hadamard_product_f32_scalar(a: &[f32], b: &[f32], out: &mut [f32]) {
let n = a.len().min(b.len()).min(out.len());
for i in 0..n { out[i] = a[i] * b[i]; }
}
pub fn hadamard_product_f32_supported(_: &HardwareDNA) -> bool { true }
pub fn add_f64_scalar(a: &[f64], b: &[f64], out: &mut [f64]) {
let n = a.len().min(b.len()).min(out.len());
for i in 0..n { out[i] = a[i] + b[i]; }
}
pub fn add_f64_supported(_: &HardwareDNA) -> bool { true }
pub fn sub_f64_scalar(a: &[f64], b: &[f64], out: &mut [f64]) {
let n = a.len().min(b.len()).min(out.len());
for i in 0..n { out[i] = a[i] - b[i]; }
}
pub fn sub_f64_supported(_: &HardwareDNA) -> bool { true }
pub fn sub_f32_scalar(a: &[f32], b: &[f32], out: &mut [f32]) {
let n = a.len().min(b.len()).min(out.len());
for i in 0..n { out[i] = a[i] - b[i]; }
}
pub fn sub_f32_supported(_: &HardwareDNA) -> bool { true }
pub fn mul_f64_scalar(a: &[f64], b: &[f64], out: &mut [f64]) {
let n = a.len().min(b.len()).min(out.len());
for i in 0..n { out[i] = a[i] * b[i]; }
}
pub fn mul_f64_supported(_: &HardwareDNA) -> bool { true }
pub fn mul_f32_scalar(a: &[f32], b: &[f32], out: &mut [f32]) {
let n = a.len().min(b.len()).min(out.len());
for i in 0..n { out[i] = a[i] * b[i]; }
}
pub fn mul_f32_supported(_: &HardwareDNA) -> bool { true }
pub fn negate_f64_scalar(a: &[f64], out: &mut [f64]) {
let n = a.len().min(out.len()); for i in 0..n { out[i] = -a[i]; }
}
pub fn negate_f64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn negate_f64_neon(a: &[f64], out: &mut [f64]) {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len().min(out.len()); let mut i = 0;
unsafe {
while i + 2 <= n {
let va = vld1q_f64(a.as_ptr().add(i)); let vneg = vnegq_f64(va);
vst1q_f64(out.as_mut_ptr().add(i), vneg); i += 2;
}
}
for j in i..n { out[j] = -a[j]; }
}
#[cfg(target_arch = "aarch64")]
pub fn negate_f64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn negate_f64_neon(_: &[f64], _: &mut [f64]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn negate_f64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn negate_f64_sse(a: &[f64], out: &mut [f64]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(out.len()); let mut i = 0;
let sign_mask = unsafe { _mm_set1_pd(-0.0f64) };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 2 <= n {
let va = _mm_loadu_pd(a.as_ptr().add(i));
_mm_storeu_pd(out.as_mut_ptr().add(i), _mm_xor_pd(va, sign_mask)); i += 2;
}
}
}
for j in i..n { out[j] = -a[j]; }
}
#[cfg(target_arch = "x86_64")]
pub fn negate_f64_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn negate_f64_sse(_: &[f64], _: &mut [f64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn negate_f64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn negate_f64_avx2(a: &[f64], out: &mut [f64]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(out.len()); let mut i = 0;
let sign_mask = unsafe { _mm256_set1_pd(-0.0f64) };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 4 <= n {
let va = _mm256_loadu_pd(a.as_ptr().add(i));
_mm256_storeu_pd(out.as_mut_ptr().add(i), _mm256_xor_pd(va, sign_mask)); i += 4;
}
}
}
for j in i..n { out[j] = -a[j]; }
}
#[cfg(target_arch = "x86_64")]
pub fn negate_f64_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn negate_f64_avx2(_: &[f64], _: &mut [f64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn negate_f64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn clamp_f64_scalar(a: &[f64], lo: f64, hi: f64, out: &mut [f64]) {
let n = a.len().min(out.len()); for i in 0..n { out[i] = a[i].clamp(lo, hi); }
}
pub fn clamp_f64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn clamp_f64_neon(a: &[f64], lo: f64, hi: f64, out: &mut [f64]) {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len().min(out.len()); let mut i = 0;
let vlo = unsafe { vdupq_n_f64(lo) }; let vhi = unsafe { vdupq_n_f64(hi) };
unsafe {
while i + 2 <= n {
let va = vld1q_f64(a.as_ptr().add(i)); let clamped = vminq_f64(vmaxq_f64(va, vlo), vhi);
vst1q_f64(out.as_mut_ptr().add(i), clamped); i += 2;
}
}
for j in i..n { out[j] = a[j].clamp(lo, hi); }
}
#[cfg(target_arch = "aarch64")]
pub fn clamp_f64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn clamp_f64_neon(_: &[f64], _: f64, _: f64, _: &mut [f64]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn clamp_f64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn clamp_f64_sse(a: &[f64], lo: f64, hi: f64, out: &mut [f64]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(out.len()); let mut i = 0;
let vlo = unsafe { _mm_set1_pd(lo) }; let vhi = unsafe { _mm_set1_pd(hi) };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 2 <= n {
let va = _mm_loadu_pd(a.as_ptr().add(i)); let clamped = _mm_min_pd(_mm_max_pd(va, vlo), vhi);
_mm_storeu_pd(out.as_mut_ptr().add(i), clamped); i += 2;
}
}
}
for j in i..n { out[j] = a[j].clamp(lo, hi); }
}
#[cfg(target_arch = "x86_64")]
pub fn clamp_f64_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn clamp_f64_sse(_: &[f64], _: f64, _: f64, _: &mut [f64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn clamp_f64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn clamp_f64_avx2(a: &[f64], lo: f64, hi: f64, out: &mut [f64]) {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(out.len()); let mut i = 0;
let vlo = unsafe { _mm256_set1_pd(lo) }; let vhi = unsafe { _mm256_set1_pd(hi) };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 4 <= n {
let va = _mm256_loadu_pd(a.as_ptr().add(i)); let clamped = _mm256_min_pd(_mm256_max_pd(va, vlo), vhi);
_mm256_storeu_pd(out.as_mut_ptr().add(i), clamped); i += 4;
}
}
}
for j in i..n { out[j] = a[j].clamp(lo, hi); }
}
#[cfg(target_arch = "x86_64")]
pub fn clamp_f64_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn clamp_f64_avx2(_: &[f64], _: f64, _: f64, _: &mut [f64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn clamp_f64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn dot_f32_scalar(a: &[f32], b: &[f32]) -> f32 {
let n = a.len().min(b.len()); let mut sum = 0.0;
for i in 0..n { sum += a[i] * b[i]; }
sum
}
pub fn dot_f32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "x86_64")]
pub fn dot_f32_sse(a: &[f32], b: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(b.len()); let mut i = 0;
let mut vacc = unsafe { _mm_setzero_ps() };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= n {
let va = _mm_loadu_ps(a.as_ptr().add(i)); let vb = _mm_loadu_ps(b.as_ptr().add(i));
vacc = _mm_add_ps(vacc, _mm_mul_ps(va, vb)); i += 4;
}
}
}
let arr: [f32; 4] = unsafe { std::mem::transmute(vacc) };
let mut sum = arr.iter().sum::<f32>();
for j in i..n { sum += a[j] * b[j]; }
sum
}
#[cfg(target_arch = "x86_64")]
pub fn dot_f32_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_f32_sse(_: &[f32], _: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn dot_f32_avx2(a: &[f32], b: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len().min(b.len()); let mut i = 0;
let mut vacc = unsafe { _mm256_setzero_ps() };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= n {
let va = _mm256_loadu_ps(a.as_ptr().add(i)); let vb = _mm256_loadu_ps(b.as_ptr().add(i));
vacc = _mm256_add_ps(vacc, _mm256_mul_ps(va, vb)); i += 8;
}
}
}
let arr: [f32; 8] = unsafe { std::mem::transmute(vacc) };
let mut sum = arr.iter().sum::<f32>();
for j in i..n { sum += a[j] * b[j]; }
sum
}
#[cfg(target_arch = "x86_64")]
pub fn dot_f32_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_f32_avx2(_: &[f32], _: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn dot_f32_neon(a: &[f32], b: &[f32]) -> f32 {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len().min(b.len()); let mut i = 0;
let mut vacc = unsafe { vdupq_n_f32(0.0) };
unsafe {
while i + 4 <= n {
let va = vld1q_f32(a.as_ptr().add(i)); let vb = vld1q_f32(b.as_ptr().add(i));
vacc = vaddq_f32(vacc, vmulq_f32(va, vb)); i += 4;
}
}
let arr: [f32; 4] = unsafe { std::mem::transmute(vacc) };
let mut sum = arr.iter().sum::<f32>();
for j in i..n { sum += a[j] * b[j]; }
sum
}
#[cfg(target_arch = "aarch64")]
pub fn dot_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn dot_f32_neon(_: &[f32], _: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn dot_f32_neon_supported(_: &HardwareDNA) -> bool { false }
pub fn reduce_sum_f32_scalar(a: &[f32]) -> f32 {
let mut sum = 0.0; for &v in a { sum += v; } sum
}
pub fn reduce_sum_f32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_f32_sse(a: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len(); let mut i = 0;
let mut vacc = unsafe { _mm_setzero_ps() };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= n {
let v = _mm_loadu_ps(a.as_ptr().add(i)); vacc = _mm_add_ps(vacc, v); i += 4;
}
}
}
let arr: [f32; 4] = unsafe { std::mem::transmute(vacc) };
let mut sum = arr.iter().sum::<f32>();
for &v in &a[i..n] { sum += v; }
sum
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_f32_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_f32_sse(_: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_f32_avx2(a: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len(); let mut i = 0;
let mut vacc = unsafe { _mm256_setzero_ps() };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= n {
let v = _mm256_loadu_ps(a.as_ptr().add(i)); vacc = _mm256_add_ps(vacc, v); i += 8;
}
}
}
let arr: [f32; 8] = unsafe { std::mem::transmute(vacc) };
let mut sum = arr.iter().sum::<f32>();
for &v in &a[i..n] { sum += v; }
sum
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_f32_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_f32_avx2(_: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_f32_neon(a: &[f32]) -> f32 {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len(); let mut i = 0;
let mut vacc = unsafe { vdupq_n_f32(0.0) };
unsafe {
while i + 4 <= n {
let v = vld1q_f32(a.as_ptr().add(i)); vacc = vaddq_f32(vacc, v); i += 4;
}
}
let arr: [f32; 4] = unsafe { std::mem::transmute(vacc) };
let mut sum = arr.iter().sum::<f32>();
for &v in &a[i..n] { sum += v; }
sum
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_f32_neon(_: &[f32]) -> f32 { 0.0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_f32_neon_supported(_: &HardwareDNA) -> bool { false }
pub fn reduce_max_f32_scalar(a: &[f32]) -> f32 {
if a.is_empty() { return f32::NEG_INFINITY; }
let mut max = a[0]; for &v in &a[1..] { if v > max { max = v; } } max
}
pub fn reduce_max_f32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_f32_sse(a: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len(); if n == 0 { return f32::NEG_INFINITY; }
let mut i = 0;
let mut vmax = unsafe { _mm_loadu_ps(a.as_ptr()) };
i += 4;
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 4 <= n {
let v = _mm_loadu_ps(a.as_ptr().add(i)); vmax = _mm_max_ps(vmax, v); i += 4;
}
}
}
let arr: [f32; 4] = unsafe { std::mem::transmute(vmax) };
let mut max = arr[0].max(arr[1]).max(arr[2]).max(arr[3]);
for &v in &a[i..n] { if v > max { max = v; } }
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_f32_sse_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "SSE2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_f32_sse(_: &[f32]) -> f32 { f32::NEG_INFINITY }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_f32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_f32_avx2(a: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let n = a.len(); if n == 0 { return f32::NEG_INFINITY; }
let mut i = 0;
let mut vmax = unsafe { _mm256_loadu_ps(a.as_ptr()) };
i += 8;
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 8 <= n {
let v = _mm256_loadu_ps(a.as_ptr().add(i)); vmax = _mm256_max_ps(vmax, v); i += 8;
}
}
}
let arr: [f32; 8] = unsafe { std::mem::transmute(vmax) };
let mut max = arr.iter().copied().fold(f32::NEG_INFINITY, f32::max);
for &v in &a[i..n] { if v > max { max = v; } }
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_f32_avx2_supported(dna: &HardwareDNA) -> bool { dna.cpu.features.iter().any(|f| f == "AVX2") }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_f32_avx2(_: &[f32]) -> f32 { f32::NEG_INFINITY }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_f32_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_f32_neon(a: &[f32]) -> f32 {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let n = a.len(); if n == 0 { return f32::NEG_INFINITY; }
let mut i = 0;
let mut vmax = unsafe { vld1q_f32(a.as_ptr()) };
i += 4;
unsafe {
while i + 4 <= n {
let v = vld1q_f32(a.as_ptr().add(i)); vmax = vmaxq_f32(vmax, v); i += 4;
}
}
let arr: [f32; 4] = unsafe { std::mem::transmute(vmax) };
let mut max = arr[0].max(arr[1]).max(arr[2]).max(arr[3]);
for &v in &a[i..n] { if v > max { max = v; } }
max
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_f32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_f32_neon(_: &[f32]) -> f32 { f32::NEG_INFINITY }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_f32_neon_supported(_: &HardwareDNA) -> bool { false }