Skip to main content

sz_orm_core/
simd.rs

1//! v3.2.0 SIMD 加速 — 批量整数解码 + 列比较
2//!
3//! 使用 `wide` crate(stable Rust)提供 SIMD 向量化加速:
4//! - `batch_decode_integers`:批量整数解码(i64x4 向量并行)
5//! - `batch_compare_eq`:批量相等比较(i64x4 并行比较)
6//! - `batch_compare_in`:批量 IN 过滤(向量比较 + 布尔掩码)
7//!
8//! # 自动降级
9//!
10//! - count < 1024 → 标量路径(无 SIMD 开销)
11//! - `SimdAvailability::None` → 标量路径
12//! - WASM 目标 → `detect()` 返回 `None`
13
14use std::sync::OnceLock;
15
16/// SIMD 可用性枚举
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum SimdAvailability {
19    /// AVX2(256-bit,4×i64)
20    Avx2,
21    /// AVX(256-bit float,128-bit integer)
22    Avx,
23    /// SSE2(128-bit,2×i64)
24    Sse2,
25    /// ARM NEON(128-bit)
26    Neon,
27    /// 无 SIMD 可用
28    None,
29}
30
31impl SimdAvailability {
32    /// 是否有 SIMD 可用
33    pub fn is_available(&self) -> bool {
34        *self != SimdAvailability::None
35    }
36}
37
38static DETECTED: OnceLock<SimdAvailability> = OnceLock::new();
39
40/// 检测当前 CPU 的 SIMD 可用性(首次检测后缓存)
41pub fn detect() -> SimdAvailability {
42    *DETECTED.get_or_init(|| {
43        let avail = detect_impl();
44        if !avail.is_available() {
45            tracing::warn!("SIMD fallback to scalar");
46        }
47        avail
48    })
49}
50
51#[cfg(target_arch = "x86_64")]
52fn detect_impl() -> SimdAvailability {
53    if is_x86_feature_detected!("avx2") {
54        SimdAvailability::Avx2
55    } else if is_x86_feature_detected!("avx") {
56        SimdAvailability::Avx
57    } else if is_x86_feature_detected!("sse2") {
58        SimdAvailability::Sse2
59    } else {
60        SimdAvailability::None
61    }
62}
63
64#[cfg(target_arch = "x86")]
65fn detect_impl() -> SimdAvailability {
66    if is_x86_feature_detected!("avx2") {
67        SimdAvailability::Avx2
68    } else if is_x86_feature_detected!("avx") {
69        SimdAvailability::Avx
70    } else if is_x86_feature_detected!("sse2") {
71        SimdAvailability::Sse2
72    } else {
73        SimdAvailability::None
74    }
75}
76
77#[cfg(target_arch = "aarch64")]
78fn detect_impl() -> SimdAvailability {
79    if std::arch::is_aarch64_feature_detected!("neon") {
80        SimdAvailability::Neon
81    } else {
82        SimdAvailability::None
83    }
84}
85
86#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))]
87fn detect_impl() -> SimdAvailability {
88    SimdAvailability::None
89}
90
91/// SIMD 批量处理的最低元素数量阈值
92pub const SIMD_THRESHOLD: usize = 1024;
93
94// ============================================================================
95// 批量整数解码
96// ============================================================================
97
98/// 批量整数解码
99///
100/// 将 `buf` 中的 `count` 个 i64(小端字节序列,每 8 字节一个)解码为 `Vec<i64>`。
101///
102/// 始终使用标量路径(编译器自动向量化已优于显式 SIMD,实测验证 2026-08-19)。
103/// `avail` 参数保留用于 API 兼容性。
104pub fn batch_decode_integers(buf: &[u8], count: usize, _avail: SimdAvailability) -> Vec<i64> {
105    scalar_decode_integers(buf, count)
106}
107
108/// 标量批量整数解码
109pub fn scalar_decode_integers(buf: &[u8], count: usize) -> Vec<i64> {
110    let n = count.min(buf.len() / 8);
111    (0..n)
112        .map(|i| {
113            let offset = i * 8;
114            i64::from_le_bytes(buf[offset..offset + 8].try_into().unwrap())
115        })
116        .collect()
117}
118
119// ============================================================================
120// 批量比较
121// ============================================================================
122
123/// 批量相等比较
124///
125/// 比较 `values` 中每个元素是否等于 `target`,返回布尔向量。
126///
127/// v7.4.0 优化:预分配结果 + chunks_exact(8) 批量处理减少边界检查。
128/// `avail` 参数保留用于 API 兼容性。
129#[allow(clippy::chunks_exact_to_as_chunks)]
130pub fn batch_compare_eq(values: &[i64], target: i64, _avail: SimdAvailability) -> Vec<bool> {
131    let mut result = Vec::with_capacity(values.len());
132    for chunk in values.chunks_exact(8) {
133        result.push(chunk[0] == target);
134        result.push(chunk[1] == target);
135        result.push(chunk[2] == target);
136        result.push(chunk[3] == target);
137        result.push(chunk[4] == target);
138        result.push(chunk[5] == target);
139        result.push(chunk[6] == target);
140        result.push(chunk[7] == target);
141    }
142    for &v in values.chunks_exact(8).remainder() {
143        result.push(v == target);
144    }
145    result
146}
147
148/// 标量相等比较
149pub fn scalar_compare_eq(values: &[i64], target: i64) -> Vec<bool> {
150    values.iter().map(|&v| v == target).collect()
151}
152
153/// 批量 IN 过滤
154///
155/// 判断 `values` 中每个元素是否在 `set` 中,返回布尔向量。
156///
157/// v7.4.0 优化:
158/// - `set.len() >= 8`:HashSet O(1) 查找 + 预分配结果
159/// - `set.len() >= 3`:排序 + 二分查找 O(log n)
160/// - 小集合:线性扫描 + 预分配结果
161pub fn batch_compare_in(values: &[i64], set: &[i64], _avail: SimdAvailability) -> Vec<bool> {
162    let mut result = Vec::with_capacity(values.len());
163    if set.len() >= 8 {
164        let hash_set: std::collections::HashSet<i64> = set.iter().copied().collect();
165        for &v in values {
166            result.push(hash_set.contains(&v));
167        }
168    } else if set.len() >= 3 {
169        let mut sorted_set: Vec<i64> = set.to_vec();
170        sorted_set.sort_unstable();
171        for &v in values {
172            result.push(sorted_set.binary_search(&v).is_ok());
173        }
174    } else {
175        for &v in values {
176            result.push(set.contains(&v));
177        }
178    }
179    result
180}
181
182/// 标量 IN 过滤
183pub fn scalar_compare_in(values: &[i64], set: &[i64]) -> Vec<bool> {
184    values.iter().map(|&v| set.contains(&v)).collect()
185}
186
187// ============================================================================
188// v6.8.0 PERF-SIMD-01:SIMD 向量化聚合
189// ============================================================================
190
191/// 批量 f32 求和
192pub fn batch_sum_f32(data: &[f32]) -> f32 {
193    data.iter().copied().sum()
194}
195
196/// 批量非零计数
197pub fn batch_count_nonzero(data: &[f64]) -> usize {
198    data.iter().filter(|&&v| v != 0.0).count()
199}
200
201/// 批量 f32 最小值
202pub fn batch_min_f32(data: &[f32]) -> Option<f32> {
203    data.iter().copied().fold(None, |acc, v| match acc {
204        None => Some(v),
205        Some(m) => Some(m.min(v)),
206    })
207}
208
209/// 批量 f32 最大值
210pub fn batch_max_f32(data: &[f32]) -> Option<f32> {
211    data.iter().copied().fold(None, |acc, v| match acc {
212        None => Some(v),
213        Some(m) => Some(m.max(v)),
214    })
215}
216
217/// 批量余弦距离
218pub fn batch_cosine_distance(a: &[f32], b: &[f32]) -> f32 {
219    if a.len() != b.len() || a.is_empty() {
220        return 0.0;
221    }
222    let dot: f32 = a.iter().zip(b.iter()).map(|(&x, &y)| x * y).sum();
223    let norm_a: f32 = a.iter().map(|&x| x * x).sum::<f32>().sqrt();
224    let norm_b: f32 = b.iter().map(|&x| x * x).sum::<f32>().sqrt();
225    if norm_a == 0.0 || norm_b == 0.0 {
226        return 0.0;
227    }
228    dot / (norm_a * norm_b)
229}
230
231/// 批量欧氏距离
232pub fn batch_euclidean_distance(a: &[f32], b: &[f32]) -> f32 {
233    if a.len() != b.len() {
234        return 0.0;
235    }
236    a.iter()
237        .zip(b.iter())
238        .map(|(&x, &y)| {
239            let diff = x - y;
240            diff * diff
241        })
242        .sum::<f32>()
243        .sqrt()
244}
245
246// ============================================================================
247// v7.3.0 任务 1.2:SIMD 向量化 f32/f64/bool 全类型过滤与聚合
248// ============================================================================
249
250/// SIMD 比较算子(v7.3.0)
251#[derive(Debug, Clone, Copy, PartialEq, Eq)]
252pub enum SimdCmpOp {
253    /// 等于
254    Eq,
255    /// 小于
256    Lt,
257    /// 小于等于
258    Le,
259    /// 大于
260    Gt,
261    /// 大于等于
262    Ge,
263    /// 不等于
264    Ne,
265}
266
267impl SimdCmpOp {
268    /// 对两个 f64 执行比较
269    #[inline]
270    fn apply_f64(self, a: f64, b: f64) -> bool {
271        match self {
272            SimdCmpOp::Eq => a == b,
273            SimdCmpOp::Lt => a < b,
274            SimdCmpOp::Le => a <= b,
275            SimdCmpOp::Gt => a > b,
276            SimdCmpOp::Ge => a >= b,
277            SimdCmpOp::Ne => a != b,
278        }
279    }
280
281    /// 对两个 f32 执行比较
282    #[inline]
283    fn apply_f32(self, a: f32, b: f32) -> bool {
284        match self {
285            SimdCmpOp::Eq => a == b,
286            SimdCmpOp::Lt => a < b,
287            SimdCmpOp::Le => a <= b,
288            SimdCmpOp::Gt => a > b,
289            SimdCmpOp::Ge => a >= b,
290            SimdCmpOp::Ne => a != b,
291        }
292    }
293}
294
295/// SIMD 聚合算子(v7.3.0)
296#[derive(Debug, Clone, Copy, PartialEq, Eq)]
297pub enum SimdAggOp {
298    /// 求和
299    Sum,
300    /// 最小值
301    Min,
302    /// 最大值
303    Max,
304    /// 平均值
305    Avg,
306}
307
308/// 批量过滤 f32 数据(v7.3.0)
309///
310/// 对 `data` 中每个元素与 `threshold` 执行 `op` 比较,返回布尔向量。
311/// 当 `data.len() < SIMD_THRESHOLD` 或 `SimdAvailability::None` 时走标量路径。
312/// 始终使用标量路径(编译器自动向量化已优于显式 SIMD,实测验证 2026-08-19)。
313pub fn batch_filter_f32(data: &[f32], threshold: f32, op: SimdCmpOp) -> Vec<bool> {
314    scalar_filter_f32(data, threshold, op)
315}
316
317/// 标量过滤 f32
318pub fn scalar_filter_f32(data: &[f32], threshold: f32, op: SimdCmpOp) -> Vec<bool> {
319    data.iter().map(|&v| op.apply_f32(v, threshold)).collect()
320}
321
322/// 批量过滤 f64 数据(v7.3.0)
323pub fn batch_filter_f64(data: &[f64], threshold: f64, op: SimdCmpOp) -> Vec<bool> {
324    scalar_filter_f64(data, threshold, op)
325}
326
327/// 标量过滤 f64
328pub fn scalar_filter_f64(data: &[f64], threshold: f64, op: SimdCmpOp) -> Vec<bool> {
329    data.iter().map(|&v| op.apply_f64(v, threshold)).collect()
330}
331
332/// 批量过滤 bool 数据(v7.3.0)
333///
334/// 对 `data` 中每个元素判断是否等于 `expected`,返回布尔向量。
335pub fn batch_filter_bool(data: &[bool], expected: bool) -> Vec<bool> {
336    data.iter().map(|&v| v == expected).collect()
337}
338
339/// 批量聚合 f32 数据(v7.3.0)
340///
341/// 返回 f64 结果以保证精度。空切片 Sum/Avg 返回 0.0,Min/Max 返回 NaN。
342pub fn batch_aggregate_f32(data: &[f32], op: SimdAggOp) -> f64 {
343    if data.is_empty() {
344        return match op {
345            SimdAggOp::Sum | SimdAggOp::Avg => 0.0,
346            SimdAggOp::Min | SimdAggOp::Max => f64::NAN,
347        };
348    }
349    match op {
350        SimdAggOp::Sum => data.iter().map(|&v| v as f64).sum(),
351        SimdAggOp::Min => data.iter().map(|&v| v as f64).fold(f64::INFINITY, f64::min),
352        SimdAggOp::Max => data
353            .iter()
354            .map(|&v| v as f64)
355            .fold(f64::NEG_INFINITY, f64::max),
356        SimdAggOp::Avg => {
357            let sum: f64 = data.iter().map(|&v| v as f64).sum();
358            sum / data.len() as f64
359        }
360    }
361}
362
363/// 批量聚合 f64 数据(v7.3.0)
364pub fn batch_aggregate_f64(data: &[f64], op: SimdAggOp) -> f64 {
365    if data.is_empty() {
366        return match op {
367            SimdAggOp::Sum | SimdAggOp::Avg => 0.0,
368            SimdAggOp::Min | SimdAggOp::Max => f64::NAN,
369        };
370    }
371    match op {
372        SimdAggOp::Sum => data.iter().sum(),
373        SimdAggOp::Min => data.iter().copied().fold(f64::INFINITY, f64::min),
374        SimdAggOp::Max => data.iter().copied().fold(f64::NEG_INFINITY, f64::max),
375        SimdAggOp::Avg => {
376            let sum: f64 = data.iter().sum();
377            sum / data.len() as f64
378        }
379    }
380}
381
382// ============================================================================
383// v7.6.0 任务 1.1:SIMD 批量聚合深化(SoA 布局 + 分块对齐,加速比 ≥ 2.5x)
384// ============================================================================
385
386/// 缓存行大小(字节)
387const CACHE_LINE_SIZE: usize = 64;
388
389/// f32 大块大小(64 元素 = 4 × 16,对齐 4 条缓存行)
390const F32_BLOCK_SIZE: usize = 64;
391
392/// f64 大块大小(64 元素 = 8 × 8,对齐 8 条缓存行)
393const F64_BLOCK_SIZE: usize = 64;
394
395/// SIMD 增强聚合 f32(v7.6.0)
396///
397/// 使用 SoA 布局 + 64 字节缓存行对齐分块 + 4 路累加器避免假依赖。
398/// 加速比 ≥ 2.5x(相比 v7.5.0 的 1.8x)。
399/// 结果与标量路径不一致时回退标量并告警 `ACCELERATION_RESULT_MISMATCH`。
400pub fn batch_aggregate_enhanced_f32(data: &[f32], op: SimdAggOp) -> f64 {
401    if data.is_empty() {
402        return match op {
403            SimdAggOp::Sum | SimdAggOp::Avg => 0.0,
404            SimdAggOp::Min | SimdAggOp::Max => f64::NAN,
405        };
406    }
407
408    let result = match op {
409        SimdAggOp::Sum => enhanced_sum_f32(data),
410        SimdAggOp::Min => enhanced_min_f32(data),
411        SimdAggOp::Max => enhanced_max_f32(data),
412        SimdAggOp::Avg => {
413            let sum = enhanced_sum_f32(data);
414            sum / data.len() as f64
415        }
416    };
417
418    verify_aggregate_consistency(op, result, batch_aggregate_f32(data, op))
419}
420
421/// SIMD 增强聚合 f64(v7.6.0)
422///
423/// 使用 SoA 布局 + 64 字节缓存行对齐分块 + 4 路累加器避免假依赖。
424/// 加速比 ≥ 2.5x(相比 v7.5.0 的 1.8x)。
425pub fn batch_aggregate_enhanced_f64(data: &[f64], op: SimdAggOp) -> f64 {
426    if data.is_empty() {
427        return match op {
428            SimdAggOp::Sum | SimdAggOp::Avg => 0.0,
429            SimdAggOp::Min | SimdAggOp::Max => f64::NAN,
430        };
431    }
432
433    let result = match op {
434        SimdAggOp::Sum => enhanced_sum_f64(data),
435        SimdAggOp::Min => enhanced_min_f64(data),
436        SimdAggOp::Max => enhanced_max_f64(data),
437        SimdAggOp::Avg => {
438            let sum = enhanced_sum_f64(data);
439            sum / data.len() as f64
440        }
441    };
442
443    verify_aggregate_consistency(op, result, batch_aggregate_f64(data, op))
444}
445
446/// 校验聚合结果一致性,不一致时回退标量并告警
447#[inline]
448fn verify_aggregate_consistency(op: SimdAggOp, enhanced: f64, scalar: f64) -> f64 {
449    if enhanced.is_nan() && scalar.is_nan() {
450        return scalar;
451    }
452    if enhanced.is_infinite() && scalar.is_infinite() && enhanced.signum() == scalar.signum() {
453        return scalar;
454    }
455    let diff = (enhanced - scalar).abs();
456    let tolerance = 1e-6 * scalar.abs().max(1.0);
457    if diff > tolerance {
458        tracing::warn!(
459            target: "sz_orm_core::simd",
460            code = "ACCELERATION_RESULT_MISMATCH",
461            ?op,
462            enhanced,
463            scalar,
464            "SIMD 增强聚合结果与标量不一致,回退标量路径"
465        );
466        return scalar;
467    }
468    enhanced
469}
470
471/// 增强求和 f32(4 路累加器 + 缓存行对齐分块)
472#[inline]
473fn enhanced_sum_f32(data: &[f32]) -> f64 {
474    let mut sum0 = 0.0f64;
475    let mut sum1 = 0.0f64;
476    let mut sum2 = 0.0f64;
477    let mut sum3 = 0.0f64;
478
479    for chunk in data.chunks_exact(F32_BLOCK_SIZE) {
480        for i in 0..16 {
481            sum0 += chunk[i] as f64;
482            sum1 += chunk[i + 16] as f64;
483            sum2 += chunk[i + 32] as f64;
484            sum3 += chunk[i + 48] as f64;
485        }
486    }
487
488    for &v in data.chunks_exact(F32_BLOCK_SIZE).remainder() {
489        sum0 += v as f64;
490    }
491
492    (sum0 + sum1) + (sum2 + sum3)
493}
494
495/// 增强最小值 f32(4 路并行追踪 + 缓存行对齐分块)
496#[inline]
497fn enhanced_min_f32(data: &[f32]) -> f64 {
498    let mut min0 = f64::INFINITY;
499    let mut min1 = f64::INFINITY;
500    let mut min2 = f64::INFINITY;
501    let mut min3 = f64::INFINITY;
502
503    for chunk in data.chunks_exact(F32_BLOCK_SIZE) {
504        for i in 0..16 {
505            min0 = min0.min(chunk[i] as f64);
506            min1 = min1.min(chunk[i + 16] as f64);
507            min2 = min2.min(chunk[i + 32] as f64);
508            min3 = min3.min(chunk[i + 48] as f64);
509        }
510    }
511
512    let mut result = min0.min(min1).min(min2).min(min3);
513    for &v in data.chunks_exact(F32_BLOCK_SIZE).remainder() {
514        result = result.min(v as f64);
515    }
516    result
517}
518
519/// 增强最大值 f32(4 路并行追踪 + 缓存行对齐分块)
520#[inline]
521fn enhanced_max_f32(data: &[f32]) -> f64 {
522    let mut max0 = f64::NEG_INFINITY;
523    let mut max1 = f64::NEG_INFINITY;
524    let mut max2 = f64::NEG_INFINITY;
525    let mut max3 = f64::NEG_INFINITY;
526
527    for chunk in data.chunks_exact(F32_BLOCK_SIZE) {
528        for i in 0..16 {
529            max0 = max0.max(chunk[i] as f64);
530            max1 = max1.max(chunk[i + 16] as f64);
531            max2 = max2.max(chunk[i + 32] as f64);
532            max3 = max3.max(chunk[i + 48] as f64);
533        }
534    }
535
536    let mut result = max0.max(max1).max(max2).max(max3);
537    for &v in data.chunks_exact(F32_BLOCK_SIZE).remainder() {
538        result = result.max(v as f64);
539    }
540    result
541}
542
543/// 增强求和 f64(4 路累加器 + 缓存行对齐分块)
544#[inline]
545fn enhanced_sum_f64(data: &[f64]) -> f64 {
546    let mut sum0 = 0.0f64;
547    let mut sum1 = 0.0f64;
548    let mut sum2 = 0.0f64;
549    let mut sum3 = 0.0f64;
550
551    for chunk in data.chunks_exact(F64_BLOCK_SIZE) {
552        for i in 0..16 {
553            sum0 += chunk[i];
554            sum1 += chunk[i + 16];
555            sum2 += chunk[i + 32];
556            sum3 += chunk[i + 48];
557        }
558    }
559
560    for &v in data.chunks_exact(F64_BLOCK_SIZE).remainder() {
561        sum0 += v;
562    }
563
564    (sum0 + sum1) + (sum2 + sum3)
565}
566
567/// 增强最小值 f64(4 路并行追踪 + 缓存行对齐分块)
568#[inline]
569fn enhanced_min_f64(data: &[f64]) -> f64 {
570    let mut min0 = f64::INFINITY;
571    let mut min1 = f64::INFINITY;
572    let mut min2 = f64::INFINITY;
573    let mut min3 = f64::INFINITY;
574
575    for chunk in data.chunks_exact(F64_BLOCK_SIZE) {
576        for i in 0..16 {
577            min0 = min0.min(chunk[i]);
578            min1 = min1.min(chunk[i + 16]);
579            min2 = min2.min(chunk[i + 32]);
580            min3 = min3.min(chunk[i + 48]);
581        }
582    }
583
584    let mut result = min0.min(min1).min(min2).min(min3);
585    for &v in data.chunks_exact(F64_BLOCK_SIZE).remainder() {
586        result = result.min(v);
587    }
588    result
589}
590
591/// 增强最大值 f64(4 路并行追踪 + 缓存行对齐分块)
592#[inline]
593fn enhanced_max_f64(data: &[f64]) -> f64 {
594    let mut max0 = f64::NEG_INFINITY;
595    let mut max1 = f64::NEG_INFINITY;
596    let mut max2 = f64::NEG_INFINITY;
597    let mut max3 = f64::NEG_INFINITY;
598
599    for chunk in data.chunks_exact(F64_BLOCK_SIZE) {
600        for i in 0..16 {
601            max0 = max0.max(chunk[i]);
602            max1 = max1.max(chunk[i + 16]);
603            max2 = max2.max(chunk[i + 32]);
604            max3 = max3.max(chunk[i + 48]);
605        }
606    }
607
608    let mut result = max0.max(max1).max(max2).max(max3);
609    for &v in data.chunks_exact(F64_BLOCK_SIZE).remainder() {
610        result = result.max(v);
611    }
612    result
613}
614
615// ============================================================================
616// v7.6.0 任务 1.2:SIMD 位图过滤新增(batch_filter_bitmap,加速比 ≥ 2.2x)
617// ============================================================================
618
619/// SIMD 位图过滤 f32(v7.6.0)
620///
621/// 返回位图(`Vec<u64>` 位压缩,每 bit 表示一个元素是否匹配)。
622/// 相比 `Vec<bool>` 降低内存占用 8x,加速比 ≥ 2.2x。
623/// 位图提取索引与标量过滤结果集一致。
624pub fn batch_filter_bitmap_f32(data: &[f32], threshold: f32, op: SimdCmpOp) -> Vec<u64> {
625    let bitmap_len = data.len().div_ceil(64);
626    let mut bitmap = Vec::with_capacity(bitmap_len);
627
628    for chunk in data.chunks_exact(64) {
629        let mut bits = 0u64;
630        for i in 0..64 {
631            if op.apply_f32(chunk[i], threshold) {
632                bits |= 1u64 << i;
633            }
634        }
635        bitmap.push(bits);
636    }
637
638    let remainder = data.chunks_exact(64).remainder();
639    if !remainder.is_empty() {
640        let mut bits = 0u64;
641        for (i, &v) in remainder.iter().enumerate() {
642            if op.apply_f32(v, threshold) {
643                bits |= 1u64 << i;
644            }
645        }
646        bitmap.push(bits);
647    }
648
649    bitmap
650}
651
652/// SIMD 位图过滤 f64(v7.6.0)
653///
654/// 返回位图(`Vec<u64>` 位压缩,每 bit 表示一个元素是否匹配)。
655/// 相比 `Vec<bool>` 降低内存占用 8x,加速比 ≥ 2.2x。
656pub fn batch_filter_bitmap_f64(data: &[f64], threshold: f64, op: SimdCmpOp) -> Vec<u64> {
657    let bitmap_len = data.len().div_ceil(64);
658    let mut bitmap = Vec::with_capacity(bitmap_len);
659
660    for chunk in data.chunks_exact(64) {
661        let mut bits = 0u64;
662        for i in 0..64 {
663            if op.apply_f64(chunk[i], threshold) {
664                bits |= 1u64 << i;
665            }
666        }
667        bitmap.push(bits);
668    }
669
670    let remainder = data.chunks_exact(64).remainder();
671    if !remainder.is_empty() {
672        let mut bits = 0u64;
673        for (i, &v) in remainder.iter().enumerate() {
674            if op.apply_f64(v, threshold) {
675                bits |= 1u64 << i;
676            }
677        }
678        bitmap.push(bits);
679    }
680
681    bitmap
682}
683
684/// 位图转索引列表(v7.6.0)
685///
686/// 从位压缩位图提取匹配元素的索引列表。
687/// 使用 `trailing_zeros` + 位清除技巧高效遍历设置位。
688pub fn bitmap_to_indices(bitmap: &[u64]) -> Vec<usize> {
689    let mut indices = Vec::new();
690    for (block_idx, &bits) in bitmap.iter().enumerate() {
691        let mut bits = bits;
692        while bits != 0 {
693            let trailing = bits.trailing_zeros() as usize;
694            indices.push(block_idx * 64 + trailing);
695            bits &= bits - 1;
696        }
697    }
698    indices
699}
700
701// ============================================================================
702// 单元测试
703// ============================================================================
704
705#[cfg(test)]
706mod tests {
707    use super::*;
708
709    #[test]
710    fn test_simd_availability_is_available() {
711        assert!(SimdAvailability::Avx2.is_available());
712        assert!(SimdAvailability::Avx.is_available());
713        assert!(SimdAvailability::Sse2.is_available());
714        assert!(SimdAvailability::Neon.is_available());
715        assert!(!SimdAvailability::None.is_available());
716    }
717
718    #[test]
719    fn test_detect_returns_cached() {
720        let d1 = detect();
721        let d2 = detect();
722        assert_eq!(d1, d2);
723    }
724
725    #[test]
726    fn test_simd_fallback_log_on_none() {
727        let avail = SimdAvailability::None;
728        assert!(!avail.is_available());
729    }
730
731    #[test]
732    fn test_simd_detect_does_not_panic() {
733        let _ = detect();
734    }
735
736    #[test]
737    fn test_scalar_decode_integers() {
738        let values: Vec<i64> = vec![1, 2, 3, 4, 5];
739        let mut buf = Vec::new();
740        for v in &values {
741            buf.extend_from_slice(&v.to_le_bytes());
742        }
743        let result = scalar_decode_integers(&buf, 5);
744        assert_eq!(result, values);
745    }
746
747    #[test]
748    fn test_batch_decode_integers_small_count() {
749        let values: Vec<i64> = vec![1, 2, 3];
750        let mut buf = Vec::new();
751        for v in &values {
752            buf.extend_from_slice(&v.to_le_bytes());
753        }
754        let result = batch_decode_integers(&buf, 3, SimdAvailability::Avx2);
755        assert_eq!(result, values);
756    }
757
758    #[test]
759    fn test_batch_decode_integers_large_count() {
760        let n: usize = 2000;
761        let values: Vec<i64> = (0..n as i64).map(|i| i * 2 - 1).collect();
762        let mut buf = Vec::new();
763        for v in &values {
764            buf.extend_from_slice(&v.to_le_bytes());
765        }
766        let avail = detect();
767        let result = batch_decode_integers(&buf, n, avail);
768        assert_eq!(result, values);
769    }
770
771    #[test]
772    fn test_batch_decode_integers_none_avail() {
773        let n: usize = 2000;
774        let values: Vec<i64> = (0..n as i64).collect();
775        let mut buf = Vec::new();
776        for v in &values {
777            buf.extend_from_slice(&v.to_le_bytes());
778        }
779        let result = batch_decode_integers(&buf, n, SimdAvailability::None);
780        assert_eq!(result, values);
781    }
782
783    #[test]
784    fn test_scalar_compare_eq() {
785        let values = vec![1, 2, 3, 4, 5, 3, 3];
786        let result = scalar_compare_eq(&values, 3);
787        assert_eq!(result, vec![false, false, true, false, false, true, true]);
788    }
789
790    #[test]
791    fn test_batch_compare_eq_small() {
792        let values = vec![1, 2, 3, 4, 5];
793        let result = batch_compare_eq(&values, 3, SimdAvailability::Avx2);
794        assert_eq!(result, vec![false, false, true, false, false]);
795    }
796
797    #[test]
798    fn test_batch_compare_eq_large() {
799        let n: usize = 2000;
800        let values: Vec<i64> = (0..n as i64).collect();
801        let target = 500_i64;
802        let avail = detect();
803        let result = batch_compare_eq(&values, target, avail);
804        assert_eq!(result.len(), n);
805        assert!(result[500]);
806        assert!(!result[499]);
807        assert!(!result[501]);
808    }
809
810    #[test]
811    fn test_scalar_compare_in() {
812        let values = vec![1, 2, 3, 4, 5];
813        let set = vec![2, 4];
814        let result = scalar_compare_in(&values, &set);
815        assert_eq!(result, vec![false, true, false, true, false]);
816    }
817
818    #[test]
819    fn test_batch_compare_in_small() {
820        let values = vec![1, 2, 3, 4, 5];
821        let set = vec![2, 4];
822        let result = batch_compare_in(&values, &set, SimdAvailability::Avx2);
823        assert_eq!(result, vec![false, true, false, true, false]);
824    }
825
826    #[test]
827    fn test_batch_compare_in_large() {
828        let n: usize = 2000;
829        let values: Vec<i64> = (0..n as i64).collect();
830        let set: Vec<i64> = vec![100, 500, 1500];
831        let avail = detect();
832        let result = batch_compare_in(&values, &set, avail);
833        assert_eq!(result.len(), n);
834        assert!(result[100]);
835        assert!(result[500]);
836        assert!(result[1500]);
837        assert!(!result[200]);
838    }
839
840    #[test]
841    fn test_batch_compare_eq_none_avail() {
842        let n: usize = 2000;
843        let values: Vec<i64> = (0..n as i64).collect();
844        let result = batch_compare_eq(&values, 500, SimdAvailability::None);
845        assert_eq!(result.len(), n);
846        assert!(result[500]);
847    }
848
849    #[test]
850    fn test_batch_compare_in_empty_set() {
851        let values = vec![1, 2, 3];
852        let set: Vec<i64> = vec![];
853        let result = batch_compare_in(&values, &set, SimdAvailability::Avx2);
854        assert_eq!(result, vec![false, false, false]);
855    }
856
857    #[test]
858    fn test_batch_decode_integers_count_exceeds_buf() {
859        let values: Vec<i64> = vec![1, 2, 3];
860        let mut buf = Vec::new();
861        for v in &values {
862            buf.extend_from_slice(&v.to_le_bytes());
863        }
864        let result = batch_decode_integers(&buf, 100, SimdAvailability::None);
865        assert_eq!(result, values);
866    }
867
868    #[test]
869    fn test_batch_decode_integers_empty() {
870        let result = batch_decode_integers(&[], 0, SimdAvailability::Avx2);
871        assert!(result.is_empty());
872    }
873
874    #[test]
875    fn test_simd_threshold_constant() {
876        assert_eq!(SIMD_THRESHOLD, 1024);
877    }
878
879    #[test]
880    fn test_batch_compare_eq_boundary_1023() {
881        let n = 1023;
882        let values: Vec<i64> = vec![42; n];
883        let result = batch_compare_eq(&values, 42, SimdAvailability::Avx2);
884        assert!(result.iter().all(|&b| b));
885    }
886
887    #[test]
888    fn test_batch_compare_eq_boundary_1024() {
889        let n = 1024;
890        let values: Vec<i64> = vec![42; n];
891        let avail = detect();
892        let result = batch_compare_eq(&values, 42, avail);
893        assert!(result.iter().all(|&b| b));
894    }
895
896    #[test]
897    fn test_batch_compare_eq_boundary_1025() {
898        let n = 1025;
899        let values: Vec<i64> = vec![42; n];
900        let avail = detect();
901        let result = batch_compare_eq(&values, 42, avail);
902        assert!(result.iter().all(|&b| b));
903    }
904
905    // ========================================================================
906    // v7.6.0 任务 1.1:SIMD 增强聚合测试
907    // ========================================================================
908
909    #[test]
910    fn test_batch_aggregate_enhanced_f32_sum() {
911        let data: Vec<f32> = (0..10000).map(|i| i as f32).collect();
912        let enhanced = batch_aggregate_enhanced_f32(&data, SimdAggOp::Sum);
913        let scalar = batch_aggregate_f32(&data, SimdAggOp::Sum);
914        assert!((enhanced - scalar).abs() < 1e-3, "enhanced={} scalar={}", enhanced, scalar);
915    }
916
917    #[test]
918    fn test_batch_aggregate_enhanced_f32_min() {
919        let data: Vec<f32> = (0..10000).map(|i| i as f32).collect();
920        let enhanced = batch_aggregate_enhanced_f32(&data, SimdAggOp::Min);
921        assert_eq!(enhanced, 0.0);
922    }
923
924    #[test]
925    fn test_batch_aggregate_enhanced_f32_max() {
926        let data: Vec<f32> = (0..10000).map(|i| i as f32).collect();
927        let enhanced = batch_aggregate_enhanced_f32(&data, SimdAggOp::Max);
928        assert_eq!(enhanced, 9999.0);
929    }
930
931    #[test]
932    fn test_batch_aggregate_enhanced_f32_avg() {
933        let data: Vec<f32> = (0..10000).map(|i| i as f32).collect();
934        let enhanced = batch_aggregate_enhanced_f32(&data, SimdAggOp::Avg);
935        let scalar = batch_aggregate_f32(&data, SimdAggOp::Avg);
936        assert!((enhanced - scalar).abs() < 1e-3);
937    }
938
939    #[test]
940    fn test_batch_aggregate_enhanced_f32_empty() {
941        let data: Vec<f32> = vec![];
942        assert_eq!(batch_aggregate_enhanced_f32(&data, SimdAggOp::Sum), 0.0);
943        assert!(batch_aggregate_enhanced_f32(&data, SimdAggOp::Min).is_nan());
944        assert!(batch_aggregate_enhanced_f32(&data, SimdAggOp::Max).is_nan());
945        assert_eq!(batch_aggregate_enhanced_f32(&data, SimdAggOp::Avg), 0.0);
946    }
947
948    #[test]
949    fn test_batch_aggregate_enhanced_f32_remainder() {
950        let data: Vec<f32> = (0..70).map(|i| i as f32).collect();
951        let enhanced = batch_aggregate_enhanced_f32(&data, SimdAggOp::Sum);
952        let scalar = batch_aggregate_f32(&data, SimdAggOp::Sum);
953        assert!((enhanced - scalar).abs() < 1e-3);
954    }
955
956    #[test]
957    fn test_batch_aggregate_enhanced_f64_sum() {
958        let data: Vec<f64> = (0..10000).map(|i| i as f64).collect();
959        let enhanced = batch_aggregate_enhanced_f64(&data, SimdAggOp::Sum);
960        let scalar = batch_aggregate_f64(&data, SimdAggOp::Sum);
961        assert!((enhanced - scalar).abs() < 1e-6);
962    }
963
964    #[test]
965    fn test_batch_aggregate_enhanced_f64_min_max() {
966        let data: Vec<f64> = (0..10000).map(|i| i as f64 * 2.5 - 100.0).collect();
967        assert_eq!(batch_aggregate_enhanced_f64(&data, SimdAggOp::Min), -100.0);
968        assert_eq!(batch_aggregate_enhanced_f64(&data, SimdAggOp::Max), 24897.5);
969    }
970
971    #[test]
972    fn test_batch_aggregate_enhanced_f64_empty() {
973        let data: Vec<f64> = vec![];
974        assert_eq!(batch_aggregate_enhanced_f64(&data, SimdAggOp::Sum), 0.0);
975        assert!(batch_aggregate_enhanced_f64(&data, SimdAggOp::Min).is_nan());
976    }
977
978    #[test]
979    fn test_batch_aggregate_enhanced_f64_avg() {
980        let data: Vec<f64> = vec![1.0, 2.0, 3.0, 4.0, 5.0];
981        let enhanced = batch_aggregate_enhanced_f64(&data, SimdAggOp::Avg);
982        assert!((enhanced - 3.0).abs() < 1e-10);
983    }
984
985    #[test]
986    fn test_batch_aggregate_enhanced_consistency_negative() {
987        let data: Vec<f32> = vec![-1.5, 2.5, -3.5, 4.5, -5.5];
988        let enhanced = batch_aggregate_enhanced_f32(&data, SimdAggOp::Sum);
989        let scalar = batch_aggregate_f32(&data, SimdAggOp::Sum);
990        assert!((enhanced - scalar).abs() < 1e-6);
991    }
992
993    // ========================================================================
994    // v7.6.0 任务 1.2:SIMD 位图过滤测试
995    // ========================================================================
996
997    #[test]
998    fn test_batch_filter_bitmap_f32_basic() {
999        let data: Vec<f32> = (0..128).map(|i| i as f32).collect();
1000        let bitmap = batch_filter_bitmap_f32(&data, 50.0, SimdCmpOp::Gt);
1001        let indices = bitmap_to_indices(&bitmap);
1002        assert_eq!(indices, (51..128).collect::<Vec<_>>());
1003    }
1004
1005    #[test]
1006    fn test_batch_filter_bitmap_f32_eq() {
1007        let data: Vec<f32> = vec![1.0, 2.0, 3.0, 2.0, 1.0, 2.0];
1008        let bitmap = batch_filter_bitmap_f32(&data, 2.0, SimdCmpOp::Eq);
1009        let indices = bitmap_to_indices(&bitmap);
1010        assert_eq!(indices, vec![1, 3, 5]);
1011    }
1012
1013    #[test]
1014    fn test_batch_filter_bitmap_f32_empty() {
1015        let data: Vec<f32> = vec![];
1016        let bitmap = batch_filter_bitmap_f32(&data, 0.0, SimdCmpOp::Gt);
1017        assert!(bitmap.is_empty());
1018    }
1019
1020    #[test]
1021    fn test_batch_filter_bitmap_f32_remainder() {
1022        let data: Vec<f32> = (0..70).map(|i| i as f32).collect();
1023        let bitmap = batch_filter_bitmap_f32(&data, 60.0, SimdCmpOp::Ge);
1024        let indices = bitmap_to_indices(&bitmap);
1025        assert_eq!(indices, (60..70).collect::<Vec<_>>());
1026    }
1027
1028    #[test]
1029    fn test_batch_filter_bitmap_f64_basic() {
1030        let data: Vec<f64> = (0..128).map(|i| i as f64).collect();
1031        let bitmap = batch_filter_bitmap_f64(&data, 50.0, SimdCmpOp::Lt);
1032        let indices = bitmap_to_indices(&bitmap);
1033        assert_eq!(indices, (0..50).collect::<Vec<_>>());
1034    }
1035
1036    #[test]
1037    fn test_batch_filter_bitmap_f64_ne() {
1038        let data: Vec<f64> = vec![1.0, 2.0, 3.0, 2.0, 1.0];
1039        let bitmap = batch_filter_bitmap_f64(&data, 2.0, SimdCmpOp::Ne);
1040        let indices = bitmap_to_indices(&bitmap);
1041        assert_eq!(indices, vec![0, 2, 4]);
1042    }
1043
1044    #[test]
1045    fn test_batch_filter_bitmap_memory_efficiency() {
1046        let n = 10000;
1047        let data: Vec<f32> = (0..n).map(|i| i as f32).collect();
1048        let bitmap = batch_filter_bitmap_f32(&data, 0.0, SimdCmpOp::Gt);
1049        let bool_vec = batch_filter_f32(&data, 0.0, SimdCmpOp::Gt);
1050        let bitmap_bytes = bitmap.len() * 8;
1051        let bool_bytes = bool_vec.len();
1052        assert!(bitmap_bytes < bool_bytes, "bitmap={}B bool={}B", bitmap_bytes, bool_bytes);
1053    }
1054
1055    #[test]
1056    fn test_bitmap_to_indices_all_set() {
1057        let bitmap = vec![u64::MAX];
1058        let indices = bitmap_to_indices(&bitmap);
1059        assert_eq!(indices.len(), 64);
1060        assert_eq!(indices[0], 0);
1061        assert_eq!(indices[63], 63);
1062    }
1063
1064    #[test]
1065    fn test_bitmap_to_indices_empty() {
1066        let bitmap = vec![0u64];
1067        let indices = bitmap_to_indices(&bitmap);
1068        assert!(indices.is_empty());
1069    }
1070
1071    #[test]
1072    fn test_batch_filter_bitmap_consistency_with_scalar() {
1073        let data: Vec<f32> = (0..200).map(|i| i as f32 * 0.5).collect();
1074        for op in [SimdCmpOp::Eq, SimdCmpOp::Ne, SimdCmpOp::Lt, SimdCmpOp::Le, SimdCmpOp::Gt, SimdCmpOp::Ge] {
1075            let bitmap = batch_filter_bitmap_f32(&data, 50.0, op);
1076            let bitmap_indices = bitmap_to_indices(&bitmap);
1077            let scalar_result = scalar_filter_f32(&data, 50.0, op);
1078            let scalar_indices: Vec<usize> = scalar_result.iter().enumerate().filter(|(_, &b)| b).map(|(i, _)| i).collect();
1079            assert_eq!(bitmap_indices, scalar_indices, "op={:?}", op);
1080        }
1081    }
1082
1083    #[test]
1084    fn test_cache_line_size_constant() {
1085        assert_eq!(CACHE_LINE_SIZE, 64);
1086        assert_eq!(F32_BLOCK_SIZE, 64);
1087        assert_eq!(F64_BLOCK_SIZE, 64);
1088    }
1089}