@@ -9,7 +9,7 @@ use hexhamming::hex_hamming_distance_pack;
99
1010// Hex sizes are character counts; byte sizes are the corresponding decoded lengths.
1111const HEX_SIZES : [ usize ; 5 ] = [ 16 , 32 , 64 , 128 , 254 ] ;
12- const BYTE_SIZES : [ usize ; 8 ] = [ 8 , 16 , 32 , 64 , 127 , 128 , 256 , 512 ] ;
12+ const BYTE_SIZES : [ usize ; 12 ] = [ 8 , 16 , 32 , 48 , 63 , 64 , 65 , 96 , 127 , 128 , 256 , 512 ] ;
1313
1414fn pseudo_random_bytes ( len : usize , seed : u64 ) -> Vec < u8 > {
1515 let mut state = seed;
@@ -58,7 +58,7 @@ fn bench_hex_by_algo(c: &mut Criterion) {
5858/// Benchmark bytes hamming distance across all available algorithms
5959fn bench_bytes_by_algo ( c : & mut Criterion ) {
6060 let algos: & [ & str ] = if cfg ! ( target_arch = "x86_64" ) {
61- & [ "classic" , "sse" , "avx2" , "avx512" ]
61+ & [ "classic" , "native" , " sse", "avx2" , "avx512" ]
6262 } else if cfg ! ( target_arch = "aarch64" ) {
6363 & [ "classic" , "native" , "neon" ]
6464 } else {
@@ -358,6 +358,83 @@ fn bench_fixed_width_array_matrix(c: &mut Criterion) {
358358 }
359359}
360360
361+ /// A/B comparison between the algorithm paths that DO and DO NOT engage the
362+ /// fixed-width cross-record scanner. `select_array_scanner_for_width` opts in
363+ /// only for `native`/`neon` on aarch64 and `native`/`avx512` on x86; other
364+ /// algorithms fall back to the per-record byte kernel. Running the same
365+ /// scenarios under both toggles measures the end-to-end dispatch alternatives.
366+ ///
367+ /// Only runs on architectures where a fixed-width scanner is available.
368+ #[ cfg( any( target_arch = "aarch64" , target_arch = "x86_64" ) ) ]
369+ fn bench_fixed_width_scanner_vs_kernel ( c : & mut Criterion ) {
370+ // The x86 comparison includes both cross-record batching and the wider
371+ // AVX-512 BITALG popcount; it is an end-to-end comparison against the
372+ // narrower AVX2 per-record fallback, not an isolated batching benchmark.
373+ let pairs: & [ ( & str , & str ) ] = if cfg ! ( target_arch = "x86_64" ) {
374+ & [ ( "avx512" , "avx2" ) ]
375+ } else {
376+ & [ ( "native" , "classic" ) ]
377+ } ;
378+
379+ for & ( scanner_algo, kernel_algo) in pairs {
380+ let scanner_ok = set_algorithm ( scanner_algo) . is_ok ( ) ;
381+ set_algorithm ( "native" ) . ok ( ) ;
382+ let kernel_ok = set_algorithm ( kernel_algo) . is_ok ( ) ;
383+ set_algorithm ( "native" ) . ok ( ) ;
384+ if !scanner_ok || !kernel_ok {
385+ continue ;
386+ }
387+
388+ for & ( role, algo) in & [ ( "scanner" , scanner_algo) , ( "kernel" , kernel_algo) ] {
389+ if set_algorithm ( algo) . is_err ( ) {
390+ continue ;
391+ }
392+ let mut group = c. benchmark_group ( format ! (
393+ "array_scanner/{scanner_algo}_vs_{kernel_algo}/{role}"
394+ ) ) ;
395+ for & width in & [ 16usize , 32 ] {
396+ // Same random-no-match scenario as the C4 baseline (see the
397+ // catalog benchmarks in `array_api/512x16_random_no_match`) to
398+ // allow before/after comparison at that data point.
399+ for & count in & [ 512usize , 1024 ] {
400+ let small = pseudo_random_bytes ( width, 0x51 + width as u64 ) ;
401+ let big = pseudo_random_bytes ( count * width, 0xA1 + width as u64 ) ;
402+ group. bench_function ( format ! ( "{width}byte/{count}/first" ) , |bencher| {
403+ bencher. iter ( || {
404+ bytes_array_first_within_dist (
405+ black_box ( & big) ,
406+ black_box ( & small) ,
407+ black_box ( 0 ) ,
408+ )
409+ } )
410+ } ) ;
411+ group. bench_function ( format ! ( "{width}byte/{count}/best" ) , |bencher| {
412+ bencher. iter ( || {
413+ bytes_array_best_within_dist (
414+ black_box ( & big) ,
415+ black_box ( & small) ,
416+ black_box ( 0 ) ,
417+ )
418+ } )
419+ } ) ;
420+ group. bench_function ( format ! ( "{width}byte/{count}/all" ) , |bencher| {
421+ bencher. iter ( || {
422+ bytes_array_all_within_dist (
423+ black_box ( & big) ,
424+ black_box ( & small) ,
425+ black_box ( 0 ) ,
426+ )
427+ } )
428+ } ) ;
429+ }
430+ }
431+ group. finish ( ) ;
432+ }
433+ }
434+
435+ set_algorithm ( "native" ) . ok ( ) ;
436+ }
437+
361438fn bench_fixed_width_parallel_crossover ( c : & mut Criterion ) {
362439 const PAR_THRESHOLDS : [ ( & str , usize ) ; 2 ] = [
363440 ( "legacy" , 5 * 1024 * 1024 ) ,
@@ -428,10 +505,23 @@ criterion_group!(
428505 bench_array_api,
429506 bench_array_random_and_boundaries,
430507 bench_fixed_width_array_matrix,
508+ bench_fixed_width_scanner_vs_kernel,
431509 bench_fixed_width_parallel_crossover,
432510 bench_hex_string_pack
433511) ;
434- #[ cfg( not( target_arch = "aarch64" ) ) ]
512+ #[ cfg( target_arch = "x86_64" ) ]
513+ criterion_group ! (
514+ benches,
515+ bench_hex_by_algo,
516+ bench_bytes_by_algo,
517+ bench_bytes_within_dist,
518+ bench_array_api,
519+ bench_array_random_and_boundaries,
520+ bench_fixed_width_array_matrix,
521+ bench_fixed_width_scanner_vs_kernel,
522+ bench_fixed_width_parallel_crossover
523+ ) ;
524+ #[ cfg( not( any( target_arch = "aarch64" , target_arch = "x86_64" ) ) ) ]
435525criterion_group ! (
436526 benches,
437527 bench_hex_by_algo,
0 commit comments