velesdb_core/
simd_dispatch.rs1#![allow(
2 clippy::cast_precision_loss,
3 clippy::cast_possible_truncation,
4 clippy::cast_sign_loss,
5 clippy::float_cmp
6)]
7#[inline]
25#[must_use]
26pub fn dot_product_dispatched(a: &[f32], b: &[f32]) -> f32 {
27 crate::simd_native::dot_product_native(a, b)
28}
29
30#[inline]
32#[must_use]
33pub fn euclidean_dispatched(a: &[f32], b: &[f32]) -> f32 {
34 crate::simd_native::euclidean_native(a, b)
35}
36
37#[inline]
39#[must_use]
40pub fn cosine_dispatched(a: &[f32], b: &[f32]) -> f32 {
41 crate::simd_native::cosine_similarity_native(a, b)
42}
43
44#[inline]
46#[must_use]
47pub fn cosine_normalized_dispatched(a: &[f32], b: &[f32]) -> f32 {
48 crate::simd_native::cosine_normalized_native(a, b)
49}
50
51#[inline]
53#[must_use]
54pub fn hamming_dispatched(a: &[f32], b: &[f32]) -> u32 {
55 #[allow(clippy::cast_sign_loss)]
56 {
59 crate::simd_native::hamming_distance_native(a, b) as u32
60 }
61}
62
63#[must_use]
65pub fn simd_features_info() -> SimdFeatures {
66 SimdFeatures::detect()
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71#[allow(clippy::struct_excessive_bools)]
72pub struct SimdFeatures {
73 pub avx512f: bool,
75 pub avx512_popcnt: bool,
77 pub avx2: bool,
79 pub popcnt: bool,
81}
82
83impl SimdFeatures {
84 #[must_use]
86 pub fn detect() -> Self {
87 #[cfg(target_arch = "x86_64")]
88 {
89 Self {
90 avx512f: is_x86_feature_detected!("avx512f"),
91 avx512_popcnt: is_x86_feature_detected!("avx512vpopcntdq"),
92 avx2: is_x86_feature_detected!("avx2"),
93 popcnt: is_x86_feature_detected!("popcnt"),
94 }
95 }
96
97 #[cfg(not(target_arch = "x86_64"))]
98 {
99 Self {
100 avx512f: false,
101 avx512_popcnt: false,
102 avx2: false,
103 popcnt: false,
104 }
105 }
106 }
107
108 #[must_use]
110 pub const fn best_instruction_set(&self) -> &'static str {
111 if self.avx512f {
112 "AVX-512"
113 } else if self.avx2 {
114 "AVX2"
115 } else {
116 "Scalar"
117 }
118 }
119}
120
121#[cfg(test)]
127fn dot_product_scalar(a: &[f32], b: &[f32]) -> f32 {
128 assert_eq!(a.len(), b.len(), "Vector length mismatch");
129 a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()
130}
131
132#[cfg(test)]
133fn euclidean_scalar(a: &[f32], b: &[f32]) -> f32 {
134 assert_eq!(a.len(), b.len(), "Vector length mismatch");
135 a.iter()
136 .zip(b.iter())
137 .map(|(x, y)| {
138 let d = x - y;
139 d * d
140 })
141 .sum::<f32>()
142 .sqrt()
143}
144
145#[cfg(test)]
146fn cosine_scalar(a: &[f32], b: &[f32]) -> f32 {
147 assert_eq!(a.len(), b.len(), "Vector length mismatch");
148 let mut dot = 0.0f32;
149 let mut norm_a = 0.0f32;
150 let mut norm_b = 0.0f32;
151
152 for (x, y) in a.iter().zip(b.iter()) {
153 dot += x * y;
154 norm_a += x * x;
155 norm_b += y * y;
156 }
157
158 let denom = (norm_a * norm_b).sqrt();
159 if denom > 0.0 {
160 dot / denom
161 } else {
162 0.0
163 }
164}
165
166#[cfg(test)]
167fn hamming_scalar(a: &[f32], b: &[f32]) -> u32 {
168 assert_eq!(a.len(), b.len(), "Vector length mismatch");
169 #[allow(clippy::cast_possible_truncation)]
170 let count = a
171 .iter()
172 .zip(b.iter())
173 .filter(|(&x, &y)| (x > 0.5) != (y > 0.5))
174 .count() as u32;
175 count
176}
177
178#[cfg(test)]
179fn cosine_normalized_scalar(a: &[f32], b: &[f32]) -> f32 {
180 dot_product_scalar(a, b)
182}
183
184pub const CACHE_LINE_SIZE: usize = 64;
186
187pub const PREFETCH_DISTANCE_768D: usize = 768 * std::mem::size_of::<f32>() / CACHE_LINE_SIZE;
190
191pub const PREFETCH_DISTANCE_384D: usize = 384 * std::mem::size_of::<f32>() / CACHE_LINE_SIZE;
193
194pub const PREFETCH_DISTANCE_1536D: usize = 1536 * std::mem::size_of::<f32>() / CACHE_LINE_SIZE;
196
197#[inline]
199#[must_use]
200pub const fn prefetch_distance(dimension: usize) -> usize {
201 (dimension * std::mem::size_of::<f32>()) / CACHE_LINE_SIZE
202}
203
204#[cfg(test)]
209#[path = "simd_dispatch_unit_tests.rs"]
210mod tests;