diff --git a/.github/workflows/benchmark-a32.yml b/.github/workflows/benchmark-a32.yml
new file mode 100644
index 00000000..aef2c825
--- /dev/null
+++ b/.github/workflows/benchmark-a32.yml
@@ -0,0 +1,29 @@
+name: Benchmark A32
+
+on: [push, pull_request, workflow_dispatch]
+
+jobs:
+ benchmark-a32:
+ name: Benchmark A32 (Armv7-A) on A64 Native
+ if: contains(toJSON(github.event.head_commit.message), 'Merge pull request ') == false
+ timeout-minutes: 30
+ runs-on: ubuntu-24.04-arm
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Download dependencies
+ run: |
+ sudo dpkg --add-architecture armhf
+ sudo apt-get update -q -y
+ sudo apt-get install -q -y libc6:armhf crossbuild-essential-armhf g++-arm-linux-gnueabihf
+
+ - name: Build and Run movemask benchmark natively in A32 mode
+ run: |
+ echo "=== Optimized Implementation (VPADD) ==="
+ make clean > /dev/null
+ make bench-movemask CROSS_COMPILE=arm-linux-gnueabihf- EXEC_WRAPPER=
+
+ echo "=== Original Implementation (VSRA Baseline) ==="
+ make clean > /dev/null
+ make bench-movemask CROSS_COMPILE=arm-linux-gnueabihf- EXEC_WRAPPER= CXXFLAGS="-DBENCH_ORIGINAL_MOVEMASK_EPI8"
diff --git a/Makefile b/Makefile
index f62bc05f..1399fa13 100644
--- a/Makefile
+++ b/Makefile
@@ -361,7 +361,20 @@ fuzz-clean:
$(RM) -r $(FUZZ_CORPUS)
$(RM) crash-* leak-* timeout-* oom-*
-.PHONY: clean check check-main check-ieee754 check-nan check-aes check-ubsan check-asan check-strict-aliasing check-uninit check-macros check-differential generate-golden coverage-report indent ieee754 nan aes fuzz fuzz-verbose fuzz-clean
+# Movemask benchmark
+BENCH_MOVEMASK_SRC = tests/bench_movemask.cpp
+BENCH_MOVEMASK_EXEC = tests/bench_movemask
+
+$(BENCH_MOVEMASK_EXEC): $(BENCH_MOVEMASK_SRC) sse2neon.h
+ $(CXX) -O3 $(ARCH_CFLAGS) $(CXXFLAGS) -I. -std=gnu++14 $(LDFLAGS) -o $@ $<
+
+bench-movemask: $(BENCH_MOVEMASK_EXEC)
+ifeq ($(processor),$(filter $(processor),aarch64 arm64 arm armv7l))
+ $(CC) $(ARCH_CFLAGS) -c sse2neon.h
+endif
+ $(EXEC_WRAPPER) $^
+
+.PHONY: clean check check-main check-ieee754 check-nan check-aes check-ubsan check-asan check-strict-aliasing check-uninit check-macros check-differential generate-golden coverage-report indent ieee754 nan aes fuzz fuzz-verbose fuzz-clean bench-movemask
clean:
$(RM) $(OBJS) $(EXEC) $(deps) sse2neon.h.gch
$(RM) $(IEEE754_OBJS) $(IEEE754_EXEC) $(ieee754_deps)
@@ -369,6 +382,7 @@ clean:
$(RM) $(AES_OBJS) $(AES_EXEC) $(aes_deps)
$(RM) $(DIFFERENTIAL_OBJS) $(DIFFERENTIAL_EXEC) $(differential_deps)
$(RM) $(FUZZ_EXEC)
+ $(RM) $(BENCH_MOVEMASK_EXEC)
-include $(deps)
-include $(ieee754_deps)
diff --git a/perf-tier.md b/perf-tier.md
index 56cc48b0..caa247c0 100644
--- a/perf-tier.md
+++ b/perf-tier.md
@@ -33,11 +33,11 @@ Cycle estimates based on ARM Cortex-A72 (ARMv8-A) Software Optimization Guide.
| Metric | Value |
|--------|-------|
-| Total SSE Intrinsics | 468 |
-| Direct Mappings (T1) | 367 (78.4%) |
-| Moderate Emulation (T2-T3) | 87 (18.6%) |
-| Complex Emulation (T4) | 14 (3.0%) |
-| Avg NEON Ops/Intrinsic | 1.89 |
+| Total SSE Intrinsics | 485 |
+| Direct Mappings (T1) | 314 (64.7%) |
+| Moderate Emulation (T2-T3) | 119 (24.5%) |
+| Complex Emulation (T4) | 52 (10.7%) |
+| Avg NEON Ops/Intrinsic | 2.90 |
### Performance Tiers
@@ -61,167 +61,173 @@ algorithms when porting performance-critical code.
| Intrinsic | NEON Ops | Notes |
|-----------|----------|-------|
-| `_mm_mpsadbw_epu8` | 22 | SAD computation, very expensive |
+| `_mm_mpsadbw_epu8` | 37 | SAD computation, very expensive |
+| `_mm_maddubs_epi16` | 22 | Multiply-add with widening |
+| `_mm_round_ps` | 22 | Rounding modes emulation |
+| `_mm_aesenclast_si128` | 20 | Use HW crypto when available |
+| `_mm_aesdeclast_si128` | 20 | Use HW crypto when available |
+| `_mm_cvtps_epi32` | 17 | |
+| `_mm_cvttpd_pi32` | 17 | |
+| `_mm_minpos_epu16` | 17 | Horizontal minimum search |
+| `_mm_movemask_epi8` | 16 | |
| `_mm_sqrt_ps` | 15 | Newton-Raphson refinement |
-| `_mm_cvttpd_pi32` | 14 | |
+| `_mm_dp_ps` | 14 | |
| `_mm_rsqrt_ps` | 13 | Newton-Raphson refinement |
| `_mm_aesdec_si128` | 12 | Use HW crypto when available |
-| `_mm_dp_ps` | 9 | |
-| `_mm_minpos_epu16` | 9 | Horizontal minimum search |
-| `_mm_aesenc_si128` | 9 | Use HW crypto when available |
-| `_mm_aesimc_si128` | 9 | Use HW crypto when available |
-| `_mm_dp_pd` | 4 | |
-| `_mm_shuffle_epi8` | 3 | |
-| `_mm_shuffle_pi8` | 3 | |
-| `_mm_aesenclast_si128` | 3 | Use HW crypto when available |
-| `_mm_aesdeclast_si128` | 3 | Use HW crypto when available |
+| `_mm_aesimc_si128` | 11 | Use HW crypto when available |
+| `_mm_shuffle_epi8` | 10 | |
### Efficient Intrinsics (T1 - Single NEON Instruction)
These intrinsics map directly to single NEON instructions:
-- Arithmetic: `_mm_abs_epi16`, `_mm_abs_epi32`, `_mm_abs_epi8`, `_mm_abs_pi16`, `_mm_abs_pi32`, `_mm_abs_pi8`, `_mm_add_epi16`, `_mm_add_epi32`, ... (+29 more)
-- Comparison: `_mm_cmpeq_epi16`, `_mm_cmpeq_epi32`, `_mm_cmpeq_epi64`, `_mm_cmpeq_epi8`, `_mm_cmpeq_pd`, `_mm_cmpeq_ps`, `_mm_cmpgt_epi16`, `_mm_cmpgt_epi32`, ... (+9 more)
-- Logical: `_mm_and_pd`, `_mm_and_ps`, `_mm_and_si128`, `_mm_andnot_pd`, `_mm_andnot_ps`, `_mm_andnot_si128`, `_mm_floor_pd`, `_mm_floor_ps`, ... (+18 more)
-- Load/Store: `_mm_load1_pd`, `_mm_load1_ps`, `_mm_load_pd`, `_mm_load_ps`, `_mm_load_si128`, `_mm_loadu_ps`, `_mm_loadu_si128`, `_mm_set1_epi16`, ... (+24 more)
-- Conversion: `_mm_cvt_ps2pi`, `_mm_cvt_si2ss`, `_mm_cvt_ss2si`, `_mm_cvtepi32_ps`, `_mm_cvtps_epi32`, `_mm_cvtps_pi16`, `_mm_cvtsd_f64`, `_mm_cvtsd_si32`, ... (+13 more)
-- Math: `_mm_ceil_pd`, `_mm_ceil_ps`, `_mm_floor_pd`, `_mm_floor_ps`, `_mm_max_epi16`, `_mm_max_epi32`, `_mm_max_epi8`, `_mm_max_epu16`, ... (+13 more)
+- Arithmetic: `_mm_abs_epi16`, `_mm_abs_epi32`, `_mm_abs_epi8`, `_mm_abs_pi16`, `_mm_abs_pi32`, `_mm_abs_pi8`, `_mm_add_epi16`, `_mm_add_epi32`, ... (+24 more)
+- Comparison: `_mm_cmpeq_epi16`, `_mm_cmpeq_epi32`, `_mm_cmpeq_epi8`, `_mm_cmpeq_ps`, `_mm_cmpgt_epi16`, `_mm_cmpgt_epi32`, `_mm_cmpgt_epi8`, `_mm_cmpgt_ps`, ... (+4 more)
+- Logical: `_mm_and_pd`, `_mm_and_ps`, `_mm_and_si128`, `_mm_andnot_pd`, `_mm_andnot_ps`, `_mm_andnot_si128`, `_mm_or_pd`, `_mm_or_ps`, ... (+15 more)
+- Load/Store: `_mm_load1_ps`, `_mm_load_ps`, `_mm_load_si128`, `_mm_loadu_ps`, `_mm_loadu_si128`, `_mm_set1_epi16`, `_mm_set1_epi32`, `_mm_set1_epi64`, ... (+19 more)
+- Conversion: `_mm_cvt_si2ss`, `_mm_cvtepi32_ps`, `_mm_cvtps_pi16`, `_mm_cvtsd_si32`, `_mm_cvtsd_si64`, `_mm_cvtsi128_si32`, `_mm_cvtsi128_si64`, `_mm_cvtsi64_ss`, ... (+6 more)
+- Math: `_mm_max_epi16`, `_mm_max_epi32`, `_mm_max_epi8`, `_mm_max_epu16`, `_mm_max_epu32`, `_mm_max_epu8`, `_mm_max_pi16`, `_mm_max_pu8`, ... (+8 more)
### Complete Tier Classification
Click to expand full list
-#### Tier 1 (367 intrinsics)
+#### Tier 1 (314 intrinsics)
`_mm_abs_epi16`, `_mm_abs_epi32`, `_mm_abs_epi8`, `_mm_abs_pi16`
`_mm_abs_pi32`, `_mm_abs_pi8`, `_mm_add_epi16`, `_mm_add_epi32`
-`_mm_add_epi64`, `_mm_add_epi8`, `_mm_add_pd`, `_mm_add_ps`, `_mm_add_sd`
-`_mm_add_si64`, `_mm_adds_epi16`, `_mm_adds_epi8`, `_mm_adds_epu16`
-`_mm_adds_epu8`, `_mm_addsub_pd`, `_mm_addsub_ps`, `_mm_and_pd`, `_mm_and_ps`
-`_mm_and_si128`, `_mm_andnot_pd`, `_mm_andnot_ps`, `_mm_andnot_si128`
-`_mm_avg_epu16`, `_mm_avg_epu8`, `_mm_avg_pu16`, `_mm_avg_pu8`
-`_mm_blendv_epi8`, `_mm_blendv_pd`, `_mm_blendv_ps`, `_mm_castpd_ps`
-`_mm_castpd_si128`, `_mm_castps_pd`, `_mm_castps_si128`, `_mm_castsi128_pd`
-`_mm_castsi128_ps`, `_mm_ceil_pd`, `_mm_ceil_ps`, `_mm_ceil_sd`
+`_mm_add_epi64`, `_mm_add_epi8`, `_mm_add_ps`, `_mm_add_si64`
+`_mm_adds_epi16`, `_mm_adds_epi8`, `_mm_adds_epu16`, `_mm_adds_epu8`
+`_mm_addsub_pd`, `_mm_addsub_ps`, `_mm_and_pd`, `_mm_and_ps`, `_mm_and_si128`
+`_mm_andnot_pd`, `_mm_andnot_ps`, `_mm_andnot_si128`, `_mm_avg_epu16`
+`_mm_avg_epu8`, `_mm_avg_pu16`, `_mm_avg_pu8`, `_mm_blendv_epi8`
+`_mm_blendv_ps`, `_mm_castpd_ps`, `_mm_castpd_si128`, `_mm_castps_pd`
+`_mm_castps_si128`, `_mm_castsi128_pd`, `_mm_castsi128_ps`, `_mm_ceil_sd`
`_mm_ceil_ss`, `_mm_clflush`, `_mm_clmulepi64_si128`, `_mm_cmpeq_epi16`
-`_mm_cmpeq_epi32`, `_mm_cmpeq_epi64`, `_mm_cmpeq_epi8`, `_mm_cmpeq_pd`
-`_mm_cmpeq_ps`, `_mm_cmpeq_sd`, `_mm_cmpeq_ss`, `_mm_cmpestra`
-`_mm_cmpestrc`, `_mm_cmpestri`, `_mm_cmpestrm`, `_mm_cmpestro`
-`_mm_cmpestrs`, `_mm_cmpestrz`, `_mm_cmpge_pd`, `_mm_cmpge_ps`
-`_mm_cmpge_sd`, `_mm_cmpge_ss`, `_mm_cmpgt_epi16`, `_mm_cmpgt_epi32`
-`_mm_cmpgt_epi64`, `_mm_cmpgt_epi8`, `_mm_cmpgt_pd`, `_mm_cmpgt_ps`
-`_mm_cmpgt_sd`, `_mm_cmpgt_ss`, `_mm_cmpistra`, `_mm_cmpistrc`
-`_mm_cmpistri`, `_mm_cmpistrm`, `_mm_cmpistro`, `_mm_cmpistrs`
-`_mm_cmpistrz`, `_mm_cmple_pd`, `_mm_cmple_ps`, `_mm_cmple_sd`
-`_mm_cmple_ss`, `_mm_cmplt_epi16`, `_mm_cmplt_epi32`, `_mm_cmplt_epi8`
-`_mm_cmplt_pd`, `_mm_cmplt_ps`, `_mm_cmplt_sd`, `_mm_cmplt_ss`
-`_mm_cmpneq_pd`, `_mm_cmpneq_ps`, `_mm_cmpneq_sd`, `_mm_cmpneq_ss`
+`_mm_cmpeq_epi32`, `_mm_cmpeq_epi8`, `_mm_cmpeq_ps`, `_mm_cmpeq_sd`
+`_mm_cmpeq_ss`, `_mm_cmpestra`, `_mm_cmpestrc`, `_mm_cmpestri`
+`_mm_cmpestrm`, `_mm_cmpestro`, `_mm_cmpestrs`, `_mm_cmpestrz`
+`_mm_cmpge_ps`, `_mm_cmpge_ss`, `_mm_cmpgt_epi16`, `_mm_cmpgt_epi32`
+`_mm_cmpgt_epi8`, `_mm_cmpgt_ps`, `_mm_cmpgt_ss`, `_mm_cmpistra`
+`_mm_cmpistrc`, `_mm_cmpistri`, `_mm_cmpistrm`, `_mm_cmpistro`
+`_mm_cmpistrs`, `_mm_cmpistrz`, `_mm_cmple_ps`, `_mm_cmple_ss`
+`_mm_cmplt_epi16`, `_mm_cmplt_epi32`, `_mm_cmplt_epi8`, `_mm_cmplt_ps`
+`_mm_cmplt_ss`, `_mm_cmpneq_ps`, `_mm_cmpneq_sd`, `_mm_cmpneq_ss`
`_mm_cmpnge_ps`, `_mm_cmpnge_sd`, `_mm_cmpnge_ss`, `_mm_cmpngt_ps`
`_mm_cmpngt_sd`, `_mm_cmpngt_ss`, `_mm_cmpnle_ps`, `_mm_cmpnle_sd`
`_mm_cmpnle_ss`, `_mm_cmpnlt_ps`, `_mm_cmpnlt_sd`, `_mm_cmpnlt_ss`
-`_mm_cmpord_sd`, `_mm_cmpord_ss`, `_mm_cmpunord_sd`, `_mm_cmpunord_ss`
-`_mm_comieq_sd`, `_mm_comieq_ss`, `_mm_comige_sd`, `_mm_comige_ss`
-`_mm_comigt_sd`, `_mm_comigt_ss`, `_mm_comile_sd`, `_mm_comile_ss`
-`_mm_comilt_sd`, `_mm_comilt_ss`, `_mm_comineq_sd`, `_mm_comineq_ss`
-`_mm_cvt_pi2ps`, `_mm_cvt_ps2pi`, `_mm_cvt_si2ss`, `_mm_cvt_ss2si`
-`_mm_cvtepi16_epi32`, `_mm_cvtepi16_epi64`, `_mm_cvtepi32_epi64`
-`_mm_cvtepi32_pd`, `_mm_cvtepi32_ps`, `_mm_cvtepi8_epi16`
+`_mm_cmpord_ss`, `_mm_cmpunord_ss`, `_mm_comieq_ss`, `_mm_comige_ss`
+`_mm_comigt_ss`, `_mm_comile_ss`, `_mm_comilt_ss`, `_mm_comineq_sd`
+`_mm_comineq_ss`, `_mm_cvt_pi2ps`, `_mm_cvt_ps2pi`, `_mm_cvt_si2ss`
+`_mm_cvt_ss2si`, `_mm_cvtepi16_epi32`, `_mm_cvtepi16_epi64`
+`_mm_cvtepi32_epi64`, `_mm_cvtepi32_ps`, `_mm_cvtepi8_epi16`
`_mm_cvtepi8_epi32`, `_mm_cvtepu16_epi32`, `_mm_cvtepu16_epi64`
`_mm_cvtepu32_epi64`, `_mm_cvtepu8_epi16`, `_mm_cvtepu8_epi32`
-`_mm_cvtpd_epi32`, `_mm_cvtpd_ps`, `_mm_cvtpi16_ps`, `_mm_cvtpi32_pd`
-`_mm_cvtpi32_ps`, `_mm_cvtpi32x2_ps`, `_mm_cvtps_epi32`, `_mm_cvtps_pd`
+`_mm_cvtpd_epi32`, `_mm_cvtpi16_ps`, `_mm_cvtpi32_ps`, `_mm_cvtpi32x2_ps`
`_mm_cvtps_pi16`, `_mm_cvtps_pi8`, `_mm_cvtpu16_ps`, `_mm_cvtsd_f64`
`_mm_cvtsd_si32`, `_mm_cvtsd_si64`, `_mm_cvtsi128_si32`, `_mm_cvtsi128_si64`
`_mm_cvtsi32_sd`, `_mm_cvtsi32_si128`, `_mm_cvtsi64_sd`, `_mm_cvtsi64_si128`
-`_mm_cvtsi64_ss`, `_mm_cvtss_f32`, `_mm_cvtss_sd`, `_mm_cvtss_si64`
-`_mm_cvtt_ps2pi`, `_mm_cvtt_ss2si`, `_mm_cvttpd_epi32`, `_mm_cvttps_epi32`
-`_mm_cvttsd_si32`, `_mm_cvttsd_si64`, `_mm_cvttss_si64`, `_mm_div_pd`
-`_mm_div_ps`, `_mm_div_ss`, `_mm_empty`, `_mm_floor_pd`, `_mm_floor_ps`
+`_mm_cvtsi64_ss`, `_mm_cvtss_f32`, `_mm_cvtss_si64`, `_mm_cvtt_ps2pi`
+`_mm_cvtt_ss2si`, `_mm_cvttpd_epi32`, `_mm_cvttps_epi32`, `_mm_cvttsd_si32`
+`_mm_cvttsd_si64`, `_mm_cvttss_si64`, `_mm_div_ss`, `_mm_empty`
`_mm_floor_sd`, `_mm_floor_ss`, `_mm_free`, `_mm_lfence`, `_mm_load1_pd`
-`_mm_load1_ps`, `_mm_load_pd`, `_mm_load_ps`, `_mm_load_sd`, `_mm_load_si128`
-`_mm_load_ss`, `_mm_loadh_pd`, `_mm_loadh_pi`, `_mm_loadl_epi64`
-`_mm_loadl_pd`, `_mm_loadl_pi`, `_mm_loadr_pd`, `_mm_loadu_pd`
-`_mm_loadu_ps`, `_mm_loadu_si128`, `_mm_loadu_si16`, `_mm_loadu_si32`
-`_mm_loadu_si64`, `_mm_max_epi16`, `_mm_max_epi32`, `_mm_max_epi8`
-`_mm_max_epu16`, `_mm_max_epu32`, `_mm_max_epu8`, `_mm_max_pi16`
-`_mm_max_pu8`, `_mm_max_sd`, `_mm_max_ss`, `_mm_mfence`, `_mm_min_epi16`
-`_mm_min_epi32`, `_mm_min_epi8`, `_mm_min_epu16`, `_mm_min_epu32`
-`_mm_min_epu8`, `_mm_min_pi16`, `_mm_min_pu8`, `_mm_min_sd`, `_mm_min_ss`
+`_mm_load1_ps`, `_mm_load_ps`, `_mm_load_si128`, `_mm_load_ss`
+`_mm_loadh_pd`, `_mm_loadh_pi`, `_mm_loadl_epi64`, `_mm_loadl_pd`
+`_mm_loadl_pi`, `_mm_loadu_pd`, `_mm_loadu_ps`, `_mm_loadu_si128`
+`_mm_loadu_si16`, `_mm_loadu_si32`, `_mm_loadu_si64`, `_mm_max_epi16`
+`_mm_max_epi32`, `_mm_max_epi8`, `_mm_max_epu16`, `_mm_max_epu32`
+`_mm_max_epu8`, `_mm_max_pi16`, `_mm_max_pu8`, `_mm_max_ss`, `_mm_mfence`
+`_mm_min_epi16`, `_mm_min_epi32`, `_mm_min_epi8`, `_mm_min_epu16`
+`_mm_min_epu32`, `_mm_min_epu8`, `_mm_min_pi16`, `_mm_min_pu8`, `_mm_min_ss`
`_mm_monitor`, `_mm_move_epi64`, `_mm_move_sd`, `_mm_move_ss`
-`_mm_movedup_pd`, `_mm_movehdup_ps`, `_mm_movehl_ps`, `_mm_moveldup_ps`
-`_mm_movelh_ps`, `_mm_movepi64_pi64`, `_mm_movpi64_epi64`, `_mm_mul_epi32`
-`_mm_mul_epu32`, `_mm_mul_pd`, `_mm_mul_ps`, `_mm_mul_sd`, `_mm_mul_ss`
-`_mm_mul_su32`, `_mm_mulhi_epu16`, `_mm_mulhi_pu16`, `_mm_mulhrs_epi16`
-`_mm_mulhrs_pi16`, `_mm_mullo_epi16`, `_mm_mullo_epi32`, `_mm_mwait`
-`_mm_or_pd`, `_mm_or_ps`, `_mm_or_si128`, `_mm_packs_epi16`
-`_mm_packs_epi32`, `_mm_packus_epi16`, `_mm_packus_epi32`, `_mm_pause`
-`_mm_popcnt_u32`, `_mm_popcnt_u64`, `_mm_prefetch`, `_mm_rcp_ss`
-`_mm_round_pd`, `_mm_round_ps`, `_mm_round_sd`, `_mm_round_ss`
-`_mm_rsqrt_ss`, `_mm_sad_epu8`, `_mm_set1_epi16`, `_mm_set1_epi32`
-`_mm_set1_epi64`, `_mm_set1_epi64x`, `_mm_set1_epi8`, `_mm_set1_pd`
-`_mm_set1_ps`, `_mm_set_epi64`, `_mm_set_epi64x`, `_mm_set_ps1`, `_mm_set_sd`
-`_mm_set_ss`, `_mm_setcsr`, `_mm_setr_epi64`, `_mm_setr_pd`, `_mm_setzero_pd`
-`_mm_setzero_ps`, `_mm_setzero_si128`, `_mm_sfence`, `_mm_shuffle_epi_0101`
+`_mm_movehl_ps`, `_mm_movelh_ps`, `_mm_movepi64_pi64`, `_mm_movpi64_epi64`
+`_mm_mul_epi32`, `_mm_mul_epu32`, `_mm_mul_ps`, `_mm_mul_sd`, `_mm_mul_ss`
+`_mm_mul_su32`, `_mm_mulhi_pu16`, `_mm_mulhrs_epi16`, `_mm_mulhrs_pi16`
+`_mm_mullo_epi16`, `_mm_mullo_epi32`, `_mm_mwait`, `_mm_or_pd`, `_mm_or_ps`
+`_mm_or_si128`, `_mm_packs_epi16`, `_mm_packs_epi32`, `_mm_packus_epi16`
+`_mm_packus_epi32`, `_mm_pause`, `_mm_prefetch`, `_mm_rcp_ss`, `_mm_round_sd`
+`_mm_round_ss`, `_mm_rsqrt_ss`, `_mm_sad_epu8`, `_mm_set1_epi16`
+`_mm_set1_epi32`, `_mm_set1_epi64`, `_mm_set1_epi64x`, `_mm_set1_epi8`
+`_mm_set1_pd`, `_mm_set1_ps`, `_mm_set_epi64`, `_mm_set_epi64x`
+`_mm_set_ps1`, `_mm_set_sd`, `_mm_set_ss`, `_mm_setcsr`, `_mm_setr_epi64`
+`_mm_setr_pd`, `_mm_setzero_pd`, `_mm_setzero_ps`, `_mm_setzero_si128`
+`_mm_sfence`, `_mm_shuffle_epi32`, `_mm_shuffle_epi_0101`
`_mm_shuffle_epi_0122`, `_mm_shuffle_epi_0321`, `_mm_shuffle_epi_1001`
`_mm_shuffle_epi_1010`, `_mm_shuffle_epi_1032`, `_mm_shuffle_epi_2103`
`_mm_shuffle_epi_2211`, `_mm_shuffle_epi_2301`, `_mm_shuffle_epi_3332`
-`_mm_shuffle_ps_0011`, `_mm_shuffle_ps_0022`, `_mm_shuffle_ps_0101`
-`_mm_shuffle_ps_0321`, `_mm_shuffle_ps_1001`, `_mm_shuffle_ps_1010`
-`_mm_shuffle_ps_1032`, `_mm_shuffle_ps_1133`, `_mm_shuffle_ps_2103`
-`_mm_shuffle_ps_2200`, `_mm_shuffle_ps_2301`, `_mm_shuffle_ps_3202`
+`_mm_shuffle_ps`, `_mm_shuffle_ps_0011`, `_mm_shuffle_ps_0022`
+`_mm_shuffle_ps_0101`, `_mm_shuffle_ps_0321`, `_mm_shuffle_ps_1001`
+`_mm_shuffle_ps_1010`, `_mm_shuffle_ps_1032`, `_mm_shuffle_ps_1133`
+`_mm_shuffle_ps_2103`, `_mm_shuffle_ps_2200`, `_mm_shuffle_ps_2301`
`_mm_shuffle_ps_3210`, `_mm_slli_epi16`, `_mm_slli_epi32`, `_mm_slli_epi64`
-`_mm_sqrt_pd`, `_mm_sqrt_sd`, `_mm_sqrt_ss`, `_mm_store_pd`, `_mm_store_pd1`
+`_mm_sqrt_sd`, `_mm_sqrt_ss`, `_mm_srli_epi16`, `_mm_srli_epi32`
+`_mm_srli_epi64`, `_mm_srli_si128`, `_mm_store_pd`, `_mm_store_pd1`
`_mm_store_ps`, `_mm_store_sd`, `_mm_store_si128`, `_mm_store_ss`
`_mm_storeh_pd`, `_mm_storeh_pi`, `_mm_storel_epi64`, `_mm_storel_pd`
`_mm_storel_pi`, `_mm_storer_pd`, `_mm_storeu_pd`, `_mm_storeu_ps`
`_mm_storeu_si128`, `_mm_storeu_si16`, `_mm_storeu_si32`, `_mm_storeu_si64`
`_mm_stream_load_si128`, `_mm_stream_pd`, `_mm_stream_pi`, `_mm_stream_ps`
`_mm_stream_si128`, `_mm_stream_si32`, `_mm_stream_si64`, `_mm_sub_epi16`
-`_mm_sub_epi32`, `_mm_sub_epi64`, `_mm_sub_epi8`, `_mm_sub_pd`, `_mm_sub_ps`
-`_mm_sub_sd`, `_mm_sub_si64`, `_mm_sub_ss`, `_mm_subs_epi16`, `_mm_subs_epi8`
+`_mm_sub_epi32`, `_mm_sub_epi64`, `_mm_sub_epi8`, `_mm_sub_ps`, `_mm_sub_sd`
+`_mm_sub_si64`, `_mm_sub_ss`, `_mm_subs_epi16`, `_mm_subs_epi8`
`_mm_subs_epu16`, `_mm_subs_epu8`, `_mm_test_all_ones`, `_mm_undefined_pd`
-`_mm_undefined_ps`, `_mm_undefined_si128`, `_mm_unpackhi_epi16`
-`_mm_unpackhi_epi32`, `_mm_unpackhi_epi64`, `_mm_unpackhi_epi8`
-`_mm_unpackhi_pd`, `_mm_unpackhi_ps`, `_mm_unpacklo_epi16`
-`_mm_unpacklo_epi32`, `_mm_unpacklo_epi64`, `_mm_unpacklo_epi8`
-`_mm_unpacklo_pd`, `_mm_unpacklo_ps`, `_mm_xor_pd`, `_mm_xor_ps`
-`_mm_xor_si128`
+`_mm_undefined_ps`, `_mm_undefined_si128`, `_mm_unpackhi_epi64`
+`_mm_unpackhi_pd`, `_mm_unpacklo_epi64`, `_mm_unpacklo_pd`, `_mm_xor_pd`
+`_mm_xor_ps`, `_mm_xor_si128`
#### Tier 2 (73 intrinsics)
-`_mm_add_ss`, `_mm_cmpnge_pd`, `_mm_cmpngt_pd`, `_mm_cmpnle_pd`
-`_mm_cmpnlt_pd`, `_mm_cmpord_pd`, `_mm_cmpord_ps`, `_mm_cmpunord_pd`
-`_mm_cmpunord_ps`, `_mm_cvtepi8_epi64`, `_mm_cvtepu8_epi64`, `_mm_cvtpi8_ps`
-`_mm_cvtpu8_ps`, `_mm_cvtsd_ss`, `_mm_div_sd`, `_mm_hadd_epi16`
-`_mm_hadd_epi32`, `_mm_hadd_pd`, `_mm_hadd_pi16`, `_mm_hadd_pi32`
-`_mm_hadd_ps`, `_mm_hadds_epi16`, `_mm_hadds_pi16`, `_mm_hsub_epi16`
-`_mm_hsub_epi32`, `_mm_hsub_pd`, `_mm_hsub_pi16`, `_mm_hsub_pi32`
-`_mm_hsub_ps`, `_mm_hsubs_epi16`, `_mm_hsubs_pi16`, `_mm_loadr_ps`
-`_mm_madd_epi16`, `_mm_maskmove_si64`, `_mm_maskmoveu_si128`, `_mm_max_pd`
-`_mm_max_ps`, `_mm_min_pd`, `_mm_min_ps`, `_mm_movemask_pd`
-`_mm_mulhi_epi16`, `_mm_set_epi16`, `_mm_set_epi32`, `_mm_set_epi8`
-`_mm_set_pd`, `_mm_set_ps`, `_mm_setr_epi16`, `_mm_setr_epi32`
-`_mm_setr_epi8`, `_mm_setr_ps`, `_mm_shuffle_ps_2001`, `_mm_shuffle_ps_2010`
-`_mm_shuffle_ps_2032`, `_mm_sign_epi16`, `_mm_sign_epi32`, `_mm_sign_epi8`
-`_mm_sign_pi16`, `_mm_sign_pi32`, `_mm_sign_pi8`, `_mm_sll_epi16`
-`_mm_sll_epi32`, `_mm_sll_epi64`, `_mm_sra_epi16`, `_mm_sra_epi32`
+`_mm_add_ss`, `_mm_alignr_epi8`, `_mm_blend_epi16`, `_mm_blend_pd`
+`_mm_blend_ps`, `_mm_blendv_pd`, `_mm_ceil_pd`, `_mm_cmpeq_epi64`
+`_mm_cmpgt_epi64`, `_mm_cmpord_ps`, `_mm_cmpunord_ps`, `_mm_comieq_sd`
+`_mm_comige_sd`, `_mm_comigt_sd`, `_mm_comile_sd`, `_mm_comilt_sd`
+`_mm_cvtepi32_pd`, `_mm_cvtepi8_epi64`, `_mm_cvtepu8_epi64`, `_mm_cvtpd_ps`
+`_mm_cvtpi32_pd`, `_mm_cvtpi8_ps`, `_mm_cvtps_pd`, `_mm_cvtpu8_ps`
+`_mm_cvtss_sd`, `_mm_div_sd`, `_mm_floor_pd`, `_mm_hadd_epi16`
+`_mm_hadd_epi32`, `_mm_hadd_pi16`, `_mm_hadd_pi32`, `_mm_hadd_ps`
+`_mm_loadr_pd`, `_mm_loadr_ps`, `_mm_madd_epi16`, `_mm_maskmove_si64`
+`_mm_maskmoveu_si128`, `_mm_max_ps`, `_mm_min_ps`, `_mm_movedup_pd`
+`_mm_movemask_pd`, `_mm_mulhi_epi16`, `_mm_mulhi_epu16`, `_mm_set_epi16`
+`_mm_set_epi32`, `_mm_set_epi8`, `_mm_set_pd`, `_mm_set_ps`, `_mm_setr_epi16`
+`_mm_setr_epi32`, `_mm_setr_epi8`, `_mm_setr_ps`, `_mm_shuffle_pi16`
+`_mm_shuffle_ps_2001`, `_mm_shuffle_ps_2010`, `_mm_shuffle_ps_2032`
+`_mm_shuffle_ps_3202`, `_mm_sll_epi16`, `_mm_sll_epi32`, `_mm_sll_epi64`
+`_mm_slli_si128`, `_mm_sqrt_pd`, `_mm_sra_epi16`, `_mm_sra_epi32`
`_mm_srai_epi16`, `_mm_srl_epi16`, `_mm_srl_epi32`, `_mm_srl_epi64`
`_mm_store_ps1`, `_mm_storer_ps`, `_mm_test_all_zeros`, `_mm_testc_si128`
`_mm_testz_si128`
-#### Tier 3 (14 intrinsics)
-
-`_mm_aeskeygenassist_si128`, `_mm_crc32_u16`, `_mm_crc32_u32`
-`_mm_crc32_u64`, `_mm_crc32_u8`, `_mm_cvtpd_pi32`, `_mm_maddubs_epi16`
-`_mm_maddubs_pi16`, `_mm_movemask_epi8`, `_mm_movemask_pi8`
-`_mm_movemask_ps`, `_mm_rcp_ps`, `_mm_sad_pu8`, `_mm_test_mix_ones_zeros`
-
-#### Tier 4 (14 intrinsics)
-
-`_mm_aesdec_si128`, `_mm_aesdeclast_si128`, `_mm_aesenc_si128`
-`_mm_aesenclast_si128`, `_mm_aesimc_si128`, `_mm_cvttpd_pi32`, `_mm_dp_pd`
-`_mm_dp_ps`, `_mm_minpos_epu16`, `_mm_mpsadbw_epu8`, `_mm_rsqrt_ps`
-`_mm_shuffle_epi8`, `_mm_shuffle_pi8`, `_mm_sqrt_ps`
+#### Tier 3 (46 intrinsics)
+
+`_mm_alignr_pi8`, `_mm_ceil_ps`, `_mm_crc32_u16`, `_mm_crc32_u32`
+`_mm_crc32_u64`, `_mm_crc32_u8`, `_mm_cvtpd_pi32`, `_mm_cvtsd_ss`
+`_mm_div_ps`, `_mm_floor_ps`, `_mm_hadd_pd`, `_mm_hadds_epi16`
+`_mm_hadds_pi16`, `_mm_hsub_epi16`, `_mm_hsub_epi32`, `_mm_hsub_pd`
+`_mm_hsub_pi16`, `_mm_hsub_pi32`, `_mm_hsub_ps`, `_mm_hsubs_epi16`
+`_mm_hsubs_pi16`, `_mm_maddubs_pi16`, `_mm_movehdup_ps`, `_mm_moveldup_ps`
+`_mm_popcnt_u32`, `_mm_popcnt_u64`, `_mm_rcp_ps`, `_mm_sad_pu8`
+`_mm_shufflehi_epi16_function`, `_mm_shufflelo_epi16_function`
+`_mm_sign_epi16`, `_mm_sign_epi32`, `_mm_sign_epi8`, `_mm_sign_pi16`
+`_mm_sign_pi32`, `_mm_sign_pi8`, `_mm_srai_epi32`, `_mm_test_mix_ones_zeros`
+`_mm_unpackhi_epi16`, `_mm_unpackhi_epi32`, `_mm_unpackhi_epi8`
+`_mm_unpackhi_ps`, `_mm_unpacklo_epi16`, `_mm_unpacklo_epi32`
+`_mm_unpacklo_epi8`, `_mm_unpacklo_ps`
+
+#### Tier 4 (52 intrinsics)
+
+`_mm_add_pd`, `_mm_add_sd`, `_mm_aesdec_si128`, `_mm_aesdeclast_si128`
+`_mm_aesenc_si128`, `_mm_aesenclast_si128`, `_mm_aesimc_si128`
+`_mm_aeskeygenassist_si128`, `_mm_cmpeq_pd`, `_mm_cmpge_pd`, `_mm_cmpge_sd`
+`_mm_cmpgt_pd`, `_mm_cmpgt_sd`, `_mm_cmple_pd`, `_mm_cmple_sd`
+`_mm_cmplt_pd`, `_mm_cmplt_sd`, `_mm_cmpneq_pd`, `_mm_cmpnge_pd`
+`_mm_cmpngt_pd`, `_mm_cmpnle_pd`, `_mm_cmpnlt_pd`, `_mm_cmpord_pd`
+`_mm_cmpord_sd`, `_mm_cmpunord_pd`, `_mm_cmpunord_sd`, `_mm_cvtps_epi32`
+`_mm_cvttpd_pi32`, `_mm_div_pd`, `_mm_dp_pd`, `_mm_dp_ps`, `_mm_insert_ps`
+`_mm_load_pd`, `_mm_load_sd`, `_mm_maddubs_epi16`, `_mm_max_pd`, `_mm_max_sd`
+`_mm_min_pd`, `_mm_min_sd`, `_mm_minpos_epu16`, `_mm_movemask_epi8`
+`_mm_movemask_pi8`, `_mm_movemask_ps`, `_mm_mpsadbw_epu8`, `_mm_mul_pd`
+`_mm_round_pd`, `_mm_round_ps`, `_mm_rsqrt_ps`, `_mm_shuffle_epi8`
+`_mm_shuffle_pi8`, `_mm_sqrt_ps`, `_mm_sub_pd`
diff --git a/sse2neon.h b/sse2neon.h
index 16ef2f83..0cd2daca 100644
--- a/sse2neon.h
+++ b/sse2neon.h
@@ -5774,33 +5774,31 @@ FORCE_INLINE int _mm_movemask_epi8(__m128i a)
return vaddv_u8(vget_low_u8(positioned)) |
(vaddv_u8(vget_high_u8(positioned)) << 8);
#else
- // ARMv7: Shift-right-accumulate (no vaddv).
- //
- // Step 1: Extract MSB of each byte
+#if defined(BENCH_ORIGINAL_MOVEMASK_EPI8)
+ // ARMv7: Shift-right-accumulate (baseline)
uint8x16_t msbs = vshrq_n_u8(input, 7);
uint64x2_t bits = vreinterpretq_u64_u8(msbs);
-
- // Step 2: Parallel bit collection via shift-right-accumulate
- //
- // Initial (8 bytes shown):
- // byte: [ 0 ][ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ]
- // value: [ 01 ][ 00 ][ 01 ][ 01 ][ 00 ][ 01 ][ 00 ][ 01 ]
- //
- // vsra(..., 7): add original + (original >> 7)
- // byte 1 gets: orig[1] + orig[0] = b1|b0 in bits [1:0]
- // byte 3 gets: orig[3] + orig[2] = b3|b2 in bits [1:0]
- // ...
- // Result: pairs combined into odd bytes
- //
- // vsra(..., 14): combine pairs -> 4 bits in bytes 3,7
- // vsra(..., 28): combine all -> 8 bits in byte 7 (actually byte 0)
bits = vsraq_n_u64(bits, bits, 7);
bits = vsraq_n_u64(bits, bits, 14);
bits = vsraq_n_u64(bits, bits, 28);
-
- // Step 3: Extract packed result from byte 0 of each half
uint8x16_t output = vreinterpretq_u8_u64(bits);
return vgetq_lane_u8(output, 0) | (vgetq_lane_u8(output, 8) << 8);
+#else
+ // ARMv7: Pairwise add
+ //
+ // Step 1: Extract MSB of each byte as 0x00 or 0xFF
+ int8x16_t mask = vshrq_n_s8(vreinterpretq_s8_u8(input), 7);
+ // Step 2: Apply powers of 2 (1, 2, 4, 8, 16, 32, 64, 128)
+ static const uint8_t w[16] = {1, 2, 4, 8, 16, 32, 64, 128,
+ 1, 2, 4, 8, 16, 32, 64, 128};
+ uint8x16_t weighted = vandq_u8(vreinterpretq_u8_s8(mask), vld1q_u8(w));
+ // Step 3: Pairwise add to accumulate the bits
+ uint8x8_t p = vpadd_u8(vget_low_u8(weighted), vget_high_u8(weighted));
+ p = vpadd_u8(p, p);
+ p = vpadd_u8(p, p);
+ // Step 4: Extract the 16-bit mask
+ return vget_lane_u16(vreinterpret_u16_u8(p), 0);
+#endif
#endif
}
diff --git a/tests/bench_movemask.cpp b/tests/bench_movemask.cpp
new file mode 100644
index 00000000..52440a68
--- /dev/null
+++ b/tests/bench_movemask.cpp
@@ -0,0 +1,237 @@
+/**
+ * Benchmark for _mm_movemask_epi8 and related movemask intrinsics.
+ *
+ * Measures three dimensions:
+ * 1. Throughput: independent calls (pipeline utilization)
+ * 2. Latency: dependent chain (true instruction latency)
+ * 3. In-context: memchr-like string search (realistic usage)
+ *
+ * Build and run:
+ * make bench-movemask # native x86
+ * make bench-movemask CROSS_COMPILE=arm-linux-gnueabihf- # ARMv7 + QEMU
+ * make bench-movemask CROSS_COMPILE=aarch64-linux-gnu- # AArch64 + QEMU
+ *
+ * Inspired by the methodology in PR #704:
+ * https://github.com/DLTcollab/sse2neon/pull/704
+ */
+
+#if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__)
+#include "sse2neon.h"
+#else
+#include
+#include
+#endif
+
+#include
+#include
+#include
+#include
+
+/* ------------------------------------------------------------------ */
+/* Helpers */
+/* ------------------------------------------------------------------ */
+
+/* Simple xorshift32 PRNG for reproducible random data. */
+static uint32_t xorshift32(uint32_t *state)
+{
+ uint32_t x = *state;
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ return *state = x;
+}
+
+/* Monotonic clock in nanoseconds. */
+static uint64_t now_ns()
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec;
+}
+
+/* Prevent dead-code elimination. */
+static volatile int sink;
+
+/* ------------------------------------------------------------------ */
+/* Throughput: independent calls */
+/* ------------------------------------------------------------------ */
+
+static double bench_throughput(const __m128i *data,
+ int n_data,
+ int64_t iters)
+{
+ uint64_t acc = 0;
+ uint64_t t0 = now_ns();
+ for (int64_t i = 0; i < iters; i++) {
+ acc += (uint64_t) _mm_movemask_epi8(data[i & (n_data - 1)]);
+ }
+ uint64_t t1 = now_ns();
+ sink = (int) acc;
+ return (double) (t1 - t0) / (double) iters;
+}
+
+/* ------------------------------------------------------------------ */
+/* Latency: dependent chain */
+/* ------------------------------------------------------------------ */
+
+static double bench_latency(int64_t iters)
+{
+ __m128i vec = _mm_set1_epi8((char) 0xA5);
+ uint64_t t0 = now_ns();
+ for (int64_t i = 0; i < iters; i++) {
+ int mask = _mm_movemask_epi8(vec);
+ /* Feed the result back as input to create a true data dependency. */
+ vec = _mm_set1_epi8((char) (mask & 0xFF));
+ }
+ uint64_t t1 = now_ns();
+ sink = _mm_movemask_epi8(vec);
+ return (double) (t1 - t0) / (double) iters;
+}
+
+/* ------------------------------------------------------------------ */
+/* In-context: memchr-like byte search using cmpeq + movemask */
+/* ------------------------------------------------------------------ */
+
+static double bench_memchr_like(const uint8_t *haystack,
+ int len,
+ uint8_t needle,
+ int64_t iters)
+{
+ __m128i target = _mm_set1_epi8((char) needle);
+ int found_count = 0;
+ uint64_t t0 = now_ns();
+ for (int64_t iter = 0; iter < iters; iter++) {
+ for (int i = 0; i <= len - 16; i += 16) {
+ __m128i chunk =
+ _mm_loadu_si128((const __m128i *) (haystack + i));
+ __m128i cmp = _mm_cmpeq_epi8(chunk, target);
+ int mask = _mm_movemask_epi8(cmp);
+ if (mask) {
+ found_count++;
+ break;
+ }
+ }
+ }
+ uint64_t t1 = now_ns();
+ sink = found_count;
+ return (double) (t1 - t0) / (double) iters;
+}
+
+/* ------------------------------------------------------------------ */
+/* Warm-up: run a few iterations to stabilise caches & branch predictors */
+/* ------------------------------------------------------------------ */
+
+static void warmup(__m128i *data, int n_data)
+{
+ uint64_t acc = 0;
+ for (int i = 0; i < n_data * 4; i++)
+ acc += (uint64_t) _mm_movemask_epi8(data[i & (n_data - 1)]);
+ sink = (int) acc;
+}
+
+/* ------------------------------------------------------------------ */
+/* Main */
+/* ------------------------------------------------------------------ */
+
+int main()
+{
+ const int64_t ITERS = 10000000; /* 10 M iterations */
+ const int N_DATA = 1024; /* must be power of 2 */
+
+ /* --- Prepare input patterns --- */
+ __m128i data_zero[N_DATA];
+ __m128i data_all[N_DATA];
+ __m128i data_alt[N_DATA];
+ __m128i data_cmpresult[N_DATA];
+ __m128i data_rand[N_DATA];
+
+ uint32_t rng = 42;
+ volatile char dyn_zero = 0; // Prevent constant-folding at -O3
+
+ for (int i = 0; i < N_DATA; i++) {
+ uint32_t r[4];
+ for (int j = 0; j < 4; j++)
+ r[j] = xorshift32(&rng);
+ data_rand[i] = _mm_loadu_si128((const __m128i *) r);
+
+ /* Derive other arrays using dyn_zero so they aren't compile-time constants */
+ data_zero[i] = _mm_set1_epi8((char) dyn_zero);
+ data_all[i] = _mm_set1_epi8((char) (dyn_zero | 0xFF));
+
+ char v80 = (char)(dyn_zero | 0x80);
+ data_alt[i] = _mm_set_epi8(
+ (char) dyn_zero, v80, (char) dyn_zero, v80,
+ (char) dyn_zero, v80, (char) dyn_zero, v80,
+ (char) dyn_zero, v80, (char) dyn_zero, v80,
+ (char) dyn_zero, v80, (char) dyn_zero, v80);
+
+ char vFF = (char)(dyn_zero | 0xFF);
+ data_cmpresult[i] = _mm_set_epi8(
+ vFF, (char) dyn_zero, vFF, (char) dyn_zero,
+ vFF, vFF, (char) dyn_zero, (char) dyn_zero,
+ vFF, vFF, vFF,
+ (char) dyn_zero, (char) dyn_zero, (char) dyn_zero, vFF, vFF);
+ }
+
+ /* --- Warm up --- */
+ warmup(data_rand, N_DATA);
+
+ /* --- Header --- */
+ printf("=== _mm_movemask_epi8 Benchmark ===\n");
+#if defined(__aarch64__) || defined(_M_ARM64)
+ printf("Architecture: AArch64\n");
+#elif defined(__arm__)
+ printf("Architecture: ARMv7-A\n");
+#elif defined(__x86_64__)
+ printf("Architecture: x86_64\n");
+#elif defined(__i386__)
+ printf("Architecture: x86 (32-bit)\n");
+#else
+ printf("Architecture: unknown\n");
+#endif
+ printf("Iterations: %lld\n\n", (long long) ITERS);
+
+ /* --- Throughput --- */
+ printf("--- Throughput (ns/op, independent calls) ---\n");
+ printf(" All-zero: %8.2f\n",
+ bench_throughput(data_zero, N_DATA, ITERS));
+ printf(" All-ones: %8.2f\n",
+ bench_throughput(data_all, N_DATA, ITERS));
+ printf(" Alternating: %8.2f\n",
+ bench_throughput(data_alt, N_DATA, ITERS));
+ printf(" Cmp-result: %8.2f\n",
+ bench_throughput(data_cmpresult, N_DATA, ITERS));
+ printf(" Random: %8.2f\n",
+ bench_throughput(data_rand, N_DATA, ITERS));
+
+ /* --- Latency --- */
+ printf("\n--- Latency (ns/op, dependent chain) ---\n");
+ printf(" Dep-chain: %8.2f\n", bench_latency(ITERS));
+
+ /* --- In-context --- */
+ printf("\n--- In-context: memchr-like search (ns/search) ---\n");
+ uint8_t haystack[4096];
+ memset(haystack, 0x42, sizeof(haystack));
+
+ /* Needle at the midpoint */
+ haystack[2048] = 0xAA;
+ printf(" Found@2048: %8.2f\n",
+ bench_memchr_like(haystack, (int) sizeof(haystack), 0xAA,
+ ITERS / 10));
+
+ /* Needle not present */
+ haystack[2048] = 0x42;
+ printf(" Not-found: %8.2f\n",
+ bench_memchr_like(haystack, (int) sizeof(haystack), 0xAA,
+ ITERS / 10));
+
+ /* Needle at the very start */
+ haystack[0] = 0xAA;
+ printf(" Found@0: %8.2f\n",
+ bench_memchr_like(haystack, (int) sizeof(haystack), 0xAA,
+ ITERS / 10));
+ haystack[0] = 0x42;
+
+ printf("\nDone.\n");
+ return 0;
+}