use himada_core::HardwareDNA;
pub fn reduce_sum_scalar(a: &[f64]) -> f64 {
let mut sum = 0.0;
for &v in a {
sum += v;
}
sum
}
pub fn reduce_sum_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_sse(a: &[f64]) -> f64 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let mut i = 0;
let mut acc = unsafe { _mm_setzero_pd() };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 2 <= a.len() {
let v = _mm_loadu_pd(a.as_ptr().add(i));
acc = _mm_add_pd(acc, v);
i += 2;
}
}
}
let tmp: [f64; 2] = unsafe { std::mem::transmute::<_, [f64; 2]>(acc) };
let mut sum = tmp[0] + tmp[1];
for &v in &a[i..] {
sum += v;
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_sse(_: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_avx2(a: &[f64]) -> f64 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let mut i = 0;
let mut acc = unsafe { _mm256_setzero_pd() };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 4 <= a.len() {
let v = _mm256_loadu_pd(a.as_ptr().add(i));
acc = _mm256_add_pd(acc, v);
i += 4;
}
}
}
let tmp: [f64; 4] = unsafe { std::mem::transmute::<_, [f64; 4]>(acc) };
let mut sum = tmp[0] + tmp[1] + tmp[2] + tmp[3];
for &v in &a[i..] {
sum += v;
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_avx2(_: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_neon(a: &[f64]) -> f64 {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let mut i = 0;
let mut acc = unsafe { vdupq_n_f64(0.0) };
unsafe {
while i + 2 <= a.len() {
let v = vld1q_f64(a.as_ptr().add(i));
acc = vaddq_f64(acc, v);
i += 2;
}
}
let tmp: [f64; 2] = unsafe { std::mem::transmute::<_, [f64; 2]>(acc) };
let mut sum = tmp[0] + tmp[1];
for &v in &a[i..] {
sum += v;
}
sum
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_neon(_: &[f64]) -> f64 { 0.0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_neon_supported(_: &HardwareDNA) -> bool { false }
pub fn reduce_max_scalar(a: &[f64]) -> f64 {
let mut max = f64::NEG_INFINITY;
for &v in a {
if v > max {
max = v;
}
}
max
}
pub fn reduce_max_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_sse(a: &[f64]) -> f64 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let mut i = 0;
let mut acc = unsafe { _mm_set1_pd(f64::NEG_INFINITY) };
unsafe {
if is_x86_feature_detected!("sse2") {
while i + 2 <= a.len() {
let v = _mm_loadu_pd(a.as_ptr().add(i));
acc = _mm_max_pd(acc, v);
i += 2;
}
}
}
let tmp: [f64; 2] = unsafe { std::mem::transmute::<_, [f64; 2]>(acc) };
let mut max = tmp[0].max(tmp[1]);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_sse(_: &[f64]) -> f64 { f64::NEG_INFINITY }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_avx2(a: &[f64]) -> f64 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
let mut i = 0;
let mut acc = unsafe { _mm256_set1_pd(f64::NEG_INFINITY) };
unsafe {
if is_x86_feature_detected!("avx2") {
while i + 4 <= a.len() {
let v = _mm256_loadu_pd(a.as_ptr().add(i));
acc = _mm256_max_pd(acc, v);
i += 4;
}
}
}
let tmp: [f64; 4] = unsafe { std::mem::transmute::<_, [f64; 4]>(acc) };
let mut max = tmp[0].max(tmp[1]).max(tmp[2]).max(tmp[3]);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_avx2(_: &[f64]) -> f64 { f64::NEG_INFINITY }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_avx2_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_neon(a: &[f64]) -> f64 {
#[cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
let mut i = 0;
let mut acc = unsafe { vdupq_n_f64(f64::NEG_INFINITY) };
unsafe {
while i + 2 <= a.len() {
let v = vld1q_f64(a.as_ptr().add(i));
acc = vmaxq_f64(acc, v);
i += 2;
}
}
let tmp: [f64; 2] = unsafe { std::mem::transmute::<_, [f64; 2]>(acc) };
let mut max = tmp[0].max(tmp[1]);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_neon(_: &[f64]) -> f64 { f64::NEG_INFINITY }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_neon_supported(_: &HardwareDNA) -> bool { false }