Skip to main content

nodedb_query/simd_filter/
runtime.rs

1// ---------------------------------------------------------------------------
2// Runtime dispatch
3// ---------------------------------------------------------------------------
4
5use super::scalar::{
6    CmpOp, scalar_cmp_f64, scalar_cmp_i64, scalar_eq_u32, scalar_ne_u32, scalar_range_i64,
7};
8
9/// SIMD runtime for filter-to-bitmask operations.
10pub struct FilterSimdRuntime {
11    /// `values[i] == target` → bit i set.
12    pub eq_u32: fn(&[u32], u32) -> Vec<u64>,
13    /// `values[i] != target` → bit i set.
14    pub ne_u32: fn(&[u32], u32) -> Vec<u64>,
15    /// `values[i] > threshold` → bit i set.
16    pub gt_f64: fn(&[f64], f64) -> Vec<u64>,
17    /// `values[i] >= threshold` → bit i set.
18    pub gte_f64: fn(&[f64], f64) -> Vec<u64>,
19    /// `values[i] < threshold` → bit i set.
20    pub lt_f64: fn(&[f64], f64) -> Vec<u64>,
21    /// `values[i] <= threshold` → bit i set.
22    pub lte_f64: fn(&[f64], f64) -> Vec<u64>,
23    /// `values[i] > threshold` → bit i set (i64).
24    pub gt_i64: fn(&[i64], i64) -> Vec<u64>,
25    /// `values[i] >= threshold` → bit i set (i64).
26    pub gte_i64: fn(&[i64], i64) -> Vec<u64>,
27    /// `values[i] < threshold` → bit i set (i64).
28    pub lt_i64: fn(&[i64], i64) -> Vec<u64>,
29    /// `values[i] <= threshold` → bit i set (i64).
30    pub lte_i64: fn(&[i64], i64) -> Vec<u64>,
31    /// `min <= values[i] <= max` → bit i set.
32    pub range_i64: fn(&[i64], i64, i64) -> Vec<u64>,
33    pub name: &'static str,
34}
35
36impl FilterSimdRuntime {
37    pub fn detect() -> Self {
38        #[cfg(target_arch = "x86_64")]
39        {
40            if std::is_x86_feature_detected!("avx512f") {
41                return Self {
42                    eq_u32: super::avx512::avx512_eq_u32,
43                    ne_u32: super::avx512::avx512_ne_u32,
44                    gt_f64: super::avx512::avx512_gt_f64,
45                    gte_f64: super::avx512::avx512_gte_f64,
46                    lt_f64: super::avx512::avx512_lt_f64,
47                    lte_f64: super::avx512::avx512_lte_f64,
48                    gt_i64: super::avx512::avx512_gt_i64,
49                    gte_i64: super::avx512::avx512_gte_i64,
50                    lt_i64: super::avx512::avx512_lt_i64,
51                    lte_i64: super::avx512::avx512_lte_i64,
52                    range_i64: super::avx512::avx512_range_i64,
53                    name: "avx512",
54                };
55            }
56            if std::is_x86_feature_detected!("avx2") {
57                return Self {
58                    eq_u32: super::avx2::avx2_eq_u32,
59                    ne_u32: super::avx2::avx2_ne_u32,
60                    gt_f64: super::avx2::avx2_gt_f64,
61                    gte_f64: super::avx2::avx2_gte_f64,
62                    lt_f64: super::avx2::avx2_lt_f64,
63                    lte_f64: super::avx2::avx2_lte_f64,
64                    gt_i64: super::avx2::avx2_gt_i64,
65                    gte_i64: super::avx2::avx2_gte_i64,
66                    lt_i64: super::avx2::avx2_lt_i64,
67                    lte_i64: super::avx2::avx2_lte_i64,
68                    range_i64: super::avx2::avx2_range_i64,
69                    name: "avx2",
70                };
71            }
72        }
73        #[cfg(target_arch = "aarch64")]
74        {
75            return Self {
76                eq_u32: super::neon::neon_eq_u32,
77                ne_u32: super::neon::neon_ne_u32,
78                gt_f64: super::neon::neon_gt_f64,
79                gte_f64: super::neon::neon_gte_f64,
80                lt_f64: super::neon::neon_lt_f64,
81                lte_f64: super::neon::neon_lte_f64,
82                gt_i64: super::neon::neon_gt_i64,
83                gte_i64: super::neon::neon_gte_i64,
84                lt_i64: super::neon::neon_lt_i64,
85                lte_i64: super::neon::neon_lte_i64,
86                range_i64: super::neon::neon_range_i64,
87                name: "neon",
88            };
89        }
90        #[cfg(target_arch = "wasm32")]
91        {
92            return Self {
93                eq_u32: super::wasm::wasm_eq_u32,
94                ne_u32: super::wasm::wasm_ne_u32,
95                gt_f64: |v, t| scalar_cmp_f64(v, t, CmpOp::Gt),
96                gte_f64: |v, t| scalar_cmp_f64(v, t, CmpOp::Gte),
97                lt_f64: |v, t| scalar_cmp_f64(v, t, CmpOp::Lt),
98                lte_f64: |v, t| scalar_cmp_f64(v, t, CmpOp::Lte),
99                gt_i64: |v, t| scalar_cmp_i64(v, t, CmpOp::Gt),
100                gte_i64: |v, t| scalar_cmp_i64(v, t, CmpOp::Gte),
101                lt_i64: |v, t| scalar_cmp_i64(v, t, CmpOp::Lt),
102                lte_i64: |v, t| scalar_cmp_i64(v, t, CmpOp::Lte),
103                range_i64: scalar_range_i64,
104                name: "wasm-simd128",
105            };
106        }
107        #[allow(unreachable_code)]
108        Self {
109            eq_u32: scalar_eq_u32,
110            ne_u32: scalar_ne_u32,
111            gt_f64: |v, t| scalar_cmp_f64(v, t, CmpOp::Gt),
112            gte_f64: |v, t| scalar_cmp_f64(v, t, CmpOp::Gte),
113            lt_f64: |v, t| scalar_cmp_f64(v, t, CmpOp::Lt),
114            lte_f64: |v, t| scalar_cmp_f64(v, t, CmpOp::Lte),
115            gt_i64: |v, t| scalar_cmp_i64(v, t, CmpOp::Gt),
116            gte_i64: |v, t| scalar_cmp_i64(v, t, CmpOp::Gte),
117            lt_i64: |v, t| scalar_cmp_i64(v, t, CmpOp::Lt),
118            lte_i64: |v, t| scalar_cmp_i64(v, t, CmpOp::Lte),
119            range_i64: scalar_range_i64,
120            name: "scalar",
121        }
122    }
123}
124
125static FILTER_RUNTIME: std::sync::OnceLock<FilterSimdRuntime> = std::sync::OnceLock::new();
126
127/// Get the global filter SIMD runtime.
128pub fn filter_runtime() -> &'static FilterSimdRuntime {
129    FILTER_RUNTIME.get_or_init(FilterSimdRuntime::detect)
130}