3333//! env -u RUSTFLAGS cargo --config .cargo/config-v4.toml run --release --example key16_match_probe
3434//! ```
3535//!
36+ //! ## Timing method
37+ //!
38+ //! Each alternative arm is timed INTERLEAVED with arm A: every round runs both
39+ //! once in each order (A-then-B, then B-then-A), and `A/B` / `A/C` is the
40+ //! median over observations of the geometric mean of the two per-round
41+ //! ratios (`paired_ns`). Drift on a shared machine then
42+ //! lands on both arms of a round instead of on one arm's whole block of runs.
43+ //! The PROVISIONAL tables below predate this (each arm was timed in its own
44+ //! block of 101 runs, and the ratio was a ratio of medians); they are kept as
45+ //! measured and are not comparable row for row with a re-run.
46+ //!
47+ //! ## Interleaved pass (PROVISIONAL)
48+ //!
49+ //! Measured 2026-10-11 with `paired_ns` (both execution orders per
50+ //! observation), same 4-core VM, rustc 1.99.0, NO other build running (load
51+ //! average 0.3 to 0.5). Still a shared virtual machine, so still PROVISIONAL,
52+ //! not the quiet-host run the W3 / W4 decision waits for. Median ratios
53+ //! (A ns / arm ns), one pass per tier:
54+ //!
55+ //! | N | stride | sel | v3 A/B | v4 A/B | v4 A/C |
56+ //! |---|---|---|---|---|---|
57+ //! | 65 536 | 16 | 1 % | 2.45 | 3.12 | - |
58+ //! | 65 536 | 16 | 50 % | 2.44 | 3.09 | - |
59+ //! | 65 536 | 32 | 1 % | - | - | 2.58 |
60+ //! | 65 536 | 32 | 50 % | - | - | 2.62 |
61+ //! | 65 536 | 512 | 1 % | - | - | 1.07 |
62+ //! | 65 536 | 512 | 50 % | - | - | 1.04 |
63+ //! | 1 048 576 | 16 | 1 % | 1.74 | 1.92 | - |
64+ //! | 1 048 576 | 16 | 50 % | 1.72 | 1.97 | - |
65+ //! | 1 048 576 | 32 | 1 % | - | - | 1.64 |
66+ //! | 1 048 576 | 32 | 50 % | - | - | 1.63 |
67+ //! | 1 048 576 | 512 | 1 % | - | - | 1.27 |
68+ //! | 1 048 576 | 512 | 50 % | - | - | 1.22 |
69+ //!
70+ //! Against the earlier passes: the v4 arm B ratios at 65 536 rows are HIGHER
71+ //! (3.12 / 3.09 against 2.13 to 2.91), the v4 arm B 1 048 576-row 50 % ratio is
72+ //! lower (1.97 against 2.19 / 2.21), and the v3 ratios sit near the top of the
73+ //! earlier range. Arm B wins at stride 16 on both tiers and arm C wins at
74+ //! stride 32, as before. One earlier pattern did NOT survive: at stride 512
75+ //! with 65 536 rows arm C is now slightly ABOVE 1 (1.07 / 1.04), where both
76+ //! earlier passes had it at or below 1 (0.92 to 1.00). A pass measured just
77+ //! before this one, which ran A first in 51 of 101 rounds, again gave 0.94 /
78+ //! 0.95. So whether the gather path pays at that size is not settled; it sits
79+ //! within the spread between passes.
80+ //!
3681//! ## PROVISIONAL results
3782//!
3883//! **PROVISIONAL.** Measured 2026-10-11 on a 4-core Intel Xeon @ 2.10 GHz
@@ -233,19 +278,44 @@ fn build(n: usize, stride: usize, p: f64, pattern: &[u8; 16], care: &[u8; 16], s
233278 bytes
234279}
235280
236- fn median_ns ( mut f : impl FnMut ( ) ) -> f64 {
281+ /// Time `a` and `b` INTERLEAVED: each observation is one A-then-B round and
282+ /// one B-then-A round, back to back, so slow drift on a shared machine (another
283+ /// build starting, a CPU frequency step) lands on both arms of the same
284+ /// observation instead of on whichever arm happened to be timed during it, and
285+ /// both execution orders weigh exactly equally in every observation (with an
286+ /// odd number of single rounds, one order would hold the median). An
287+ /// observation's arm time is the mean of its two runs and its ratio is the
288+ /// geometric mean of the two per-round ratios. Returns the median of each arm
289+ /// and the median ratio `a / b`; the ratio is the decision statistic, the two
290+ /// medians are context.
291+ fn paired_ns ( mut a : impl FnMut ( ) , mut b : impl FnMut ( ) ) -> ( f64 , f64 , f64 ) {
237292 for _ in 0 ..WARMUP {
293+ a ( ) ;
294+ b ( ) ;
295+ }
296+ let time = |f : & mut dyn FnMut ( ) | {
297+ let t = Instant :: now ( ) ;
238298 f ( ) ;
299+ t. elapsed ( ) . as_nanos ( ) as f64
300+ } ;
301+ let mut ta = Vec :: with_capacity ( REPS ) ;
302+ let mut tb = Vec :: with_capacity ( REPS ) ;
303+ let mut ratios = Vec :: with_capacity ( REPS ) ;
304+ for _ in 0 ..REPS {
305+ let a1 = time ( & mut a) ;
306+ let b1 = time ( & mut b) ;
307+ let b2 = time ( & mut b) ;
308+ let a2 = time ( & mut a) ;
309+ ta. push ( ( a1 + a2) / 2.0 ) ;
310+ tb. push ( ( b1 + b2) / 2.0 ) ;
311+ ratios. push ( ( ( a1 / b1) * ( a2 / b2) ) . sqrt ( ) ) ;
239312 }
240- let mut ts: Vec < f64 > = ( 0 ..REPS )
241- . map ( |_| {
242- let t = Instant :: now ( ) ;
243- f ( ) ;
244- t. elapsed ( ) . as_nanos ( ) as f64
245- } )
246- . collect ( ) ;
247- ts. sort_by ( |a, b| a. partial_cmp ( b) . unwrap ( ) ) ;
248- ts[ REPS / 2 ]
313+ ( median ( & mut ta) , median ( & mut tb) , median ( & mut ratios) )
314+ }
315+
316+ fn median ( v : & mut [ f64 ] ) -> f64 {
317+ v. sort_by ( |a, b| a. partial_cmp ( b) . unwrap ( ) ) ;
318+ v[ v. len ( ) / 2 ]
249319}
250320
251321fn main ( ) {
@@ -303,25 +373,38 @@ fn main() {
303373
304374 drop ( shifted) ;
305375
306- let mut out = vec ! [ 0u64 ; wlen] ;
307- let a_ns = median_ns ( || {
308- ternary_match_strided16_to_mask ( black_box ( & bytes) , 0 , stride, n, & pattern, & care, & mut out) ;
309- black_box ( & out) ;
310- } ) ;
311- let b_ns = ( stride == 16 ) . then ( || {
312- median_ns ( || {
313- arm_b ( black_box ( & bytes) , 0 , n, & pattern, & care, & mut out) ;
314- black_box ( & out) ;
315- } )
316- } ) ;
317- let c_ns = ( stride > 16 && C_AVAILABLE ) . then ( || {
318- median_ns ( || {
319- arm_c ( black_box ( & bytes) , 0 , stride, n, & pattern, & care, & mut out) ;
320- black_box ( & out) ;
321- } )
322- } ) ;
376+ let mut out_a = vec ! [ 0u64 ; wlen] ;
377+ let mut out_x = vec ! [ 0u64 ; wlen] ;
378+ let run_a = |out : & mut [ u64 ] | {
379+ ternary_match_strided16_to_mask ( black_box ( & bytes) , 0 , stride, n, & pattern, & care, out) ;
380+ black_box ( & * out) ;
381+ } ;
382+ // Each alternative is timed interleaved with its own run of arm
383+ // A; arm A alone is timed only where no alternative applies.
384+ let ( a_ns, b_ns, c_ns, b_ratio, c_ratio) = if stride == 16 {
385+ let ( a, b, r) = paired_ns (
386+ || run_a ( & mut out_a) ,
387+ || {
388+ arm_b ( black_box ( & bytes) , 0 , n, & pattern, & care, & mut out_x) ;
389+ black_box ( & out_x) ;
390+ } ,
391+ ) ;
392+ ( a, Some ( b) , None , Some ( r) , None )
393+ } else if C_AVAILABLE {
394+ let ( a, c, r) = paired_ns (
395+ || run_a ( & mut out_a) ,
396+ || {
397+ arm_c ( black_box ( & bytes) , 0 , stride, n, & pattern, & care, & mut out_x) ;
398+ black_box ( & out_x) ;
399+ } ,
400+ ) ;
401+ ( a, None , Some ( c) , None , Some ( r) )
402+ } else {
403+ let ( a, _, _) = paired_ns ( || run_a ( & mut out_a) , || run_a ( & mut out_x) ) ;
404+ ( a, None , None , None , None )
405+ } ;
323406 let fmt = |v : Option < f64 > | v. map_or ( "-" . to_string ( ) , |x| format ! ( "{x:.0}" ) ) ;
324- let ratio = |v : Option < f64 > | v. map_or ( "-" . to_string ( ) , |x| format ! ( "{:.2}" , a_ns / x ) ) ;
407+ let ratio = |v : Option < f64 > | v. map_or ( "-" . to_string ( ) , |x| format ! ( "{x :.2}" ) ) ;
325408 println ! (
326409 "{:>9} {:>6} {:>4.0}% | {:>10.0} {:>10} {:>10} | {:>7} {:>7}" ,
327410 n,
@@ -330,8 +413,8 @@ fn main() {
330413 a_ns,
331414 fmt( b_ns) ,
332415 fmt( c_ns) ,
333- ratio( b_ns ) ,
334- ratio( c_ns )
416+ ratio( b_ratio ) ,
417+ ratio( c_ratio )
335418 ) ;
336419 }
337420 }
0 commit comments