1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
//! Runtime CPU-feature detection and the test-only scalar override.
//!
//! [`detect_features`] probes the host once via `std::sync::OnceLock` and
//! returns a [`CpuFeatures`] snapshot. The per-metric dispatch in
//! [`crate::metrics`] consults it to pick AVX2 (x86_64), NEON (aarch64), or
//! the scalar reference. `force_scalar` is a sticky global override used
//! by tests to exercise the scalar path on a host that would otherwise pick
//! a SIMD kernel.
use ;
use OnceLock;
/// Snapshot of the host CPU features [`detect_features`] cares about.
///
/// The struct is intentionally small and `Copy`: it is read on the hot
/// path of every distance call. New fields will be added in additive
/// releases — match on it exhaustively at your own risk.
///
/// The `forced_scalar` field reflects the value of the override at the
/// moment [`detect_features`] returned. Do not cache a [`CpuFeatures`]
/// across a `force_scalar` call: call [`detect_features`] each time
/// you need a fresh view.
///
/// # Examples
///
/// ```
/// let features = iqdb_distance::detect_features();
/// // Repeated calls return the same value (snapshot is cached).
/// assert_eq!(features, iqdb_distance::detect_features());
/// ```
static CPU_FEATURES: = new;
static FORCED_SCALAR: AtomicBool = new;
/// Return the host CPU-feature snapshot, computing it on first call.
///
/// The probe runs at most once per process; subsequent calls return the
/// cached value. The `forced_scalar` field reflects the *current* state of
/// the `force_scalar` override, so the snapshot remains accurate even if
/// the override is set after the probe ran.
///
/// # Examples
///
/// ```
/// let features = iqdb_distance::detect_features();
/// // On a host without AVX2 the flag is false; on a host without NEON
/// // the flag is false. Both fields are always observable.
/// let _ = (features.avx2, features.neon, features.forced_scalar);
/// ```
/// Return `true` if `force_scalar` has been called in this process.
///
/// Reads an atomic flag — cheap, allocation-free, monotonic once set.
/// `Relaxed` is sufficient: the flag is set-once `false → true` and the
/// test harness coordinates the set/observe boundary through
/// `std::sync::Once`, whose `call_once` provides happens-before for
/// observers.
///
/// # Examples
///
/// ```
/// // This crate never calls `force_scalar` itself, so the flag is normally
/// // false unless a test has set it.
/// let _ = iqdb_distance::forced_scalar();
/// ```
/// Force every dispatched distance call in this process onto the scalar
/// reference path.
///
/// The flag is **sticky**: once set, it remains set for the lifetime of
/// the process. There is intentionally no `unforce_scalar` — the override
/// exists so test suites can exercise the scalar path on hardware that
/// would otherwise pick a SIMD kernel, and a sticky flag keeps the test
/// state visible.
///
/// Available only when the crate is built with the `testing` feature. A
/// production build cannot reach the override, so SIMD cannot be disabled
/// at runtime by accident.
///
/// # Examples
///
/// ```
/// # #[cfg(feature = "testing")]
/// # {
/// use iqdb_distance::{Cosine, Distance};
///
/// let a = [1.0_f32, 0.0];
/// let b = [0.0_f32, 1.0];
/// let before = Cosine::compute(&a, &b).expect("valid pair");
///
/// // Calling `force_scalar` makes every subsequent call go scalar.
/// // iqdb_distance::force_scalar();
///
/// let after = Cosine::compute(&a, &b).expect("valid pair");
/// assert!((before - after).abs() < 1e-6);
/// # }
/// ```
/// The kernel a distance call would dispatch to right now.
///
/// Held internally and consumed by the per-metric dispatch in
/// [`crate::metrics`] via [`select_kernel`]. Variants exist only on
/// architectures where they are reachable, so `match` arms in each
/// metric's `dispatch` stay exhaustive without an `_` fallback that
/// could mask a routing mistake.
pub
/// Decide which kernel a distance call should route to, given a snapshot
/// of the host CPU features.
///
/// This is the **single source of truth** for the dispatch decision. The
/// per-metric `dispatch` fns in [`crate::metrics`] and the testing-only
/// [`which_kernel`] accessor both call this function — they cannot drift,
/// so the differential test's "SIMD actually ran" assertion is asserting
/// the real path, not a copy of it.
pub
/// Return the kernel a distance call would dispatch to right now, as a
/// short identifier: `"scalar"`, `"avx2"`, or `"neon"`.
///
/// This accessor exists so the differential SIMD-vs-scalar test can prove
/// the dispatcher actually routed to the host's SIMD kernel before
/// gathering "SIMD" samples — without this, a runtime detection
/// regression that silently fell back to scalar would let the test pass
/// vacuously (scalar-vs-scalar comparison).
///
/// Built only under `cfg(any(test, feature = "testing"))`. **Not part of
/// the stable public surface** — the return type and strings are
/// testing-internals and may change.
///
/// Internally delegates to the crate-private `select_kernel`, the same
/// function the real dispatch path uses, so the test cannot disagree
/// with reality.
///
/// # Examples
///
/// ```
/// # #[cfg(feature = "testing")]
/// # {
/// // On any host the accessor returns one of "scalar", "avx2", "neon".
/// let kernel = iqdb_distance::which_kernel();
/// assert!(matches!(kernel, "scalar" | "avx2" | "neon"));
/// # }
/// ```