Last Updated: 2026-07-15 (perf: add dedicated kpm_bench.rs — #225)
This document tracks the performance of critical image processing and pattern matching functions in the webarkitlib_rs core crate.
The following benchmarks were conducted on an x86_64 system with SSE4.1 support enabled via the simd feature.
| Function | Implementation | Median Time | Speedup |
|---|---|---|---|
rgba_to_gray |
Scalar | 629.57 µs | - |
| SIMD (SSE4.1) | 238.42 µs | 2.64x | |
dot_product |
Scalar | 326.45 ns | - |
| SIMD (SSE4.1) | 60.24 ns | 5.42x | |
box_filter_h |
Scalar | 1.8386 ms | - |
| SIMD (SSE4.1) | 1.7245 ms | 1.07x | |
box_filter_v |
Scalar | 2.3240 ms | - |
| SIMD (SSE4.1) | 765.77 µs | 3.03x |
dot_product: The most significant gain (6.4x) was achieved here. As a purely compute-bound task processingi16values, it maps perfectly to 128-bit SIMD registers (processing 8 elements at once).rgba_to_gray: Doubling the speed of the grayscale conversion is a major win for the main processing pipeline. Further gains might be limited by memory bandwidth.box_filter:- Vertical Pass: Shows a strong ~3x speedup by processing 16 columns of pixels in parallel.
- Horizontal Pass: Currently shows no improvement. This is common in horizontal filters due to the overhead of unaligned memory access patterns or being entirely memory-bound.
To run these benchmarks on your own machine:
# Run with SIMD optimizations enabled (requires SSE4.1 on x86)
cargo bench --features simd --bench simd_bench
# Run without SIMD (Scalar only)
cargo bench --bench simd_benchThe crate registers these benches (all harness = false):
| Bench | Measures |
|---|---|
simd_bench |
Scalar vs SIMD kernels — the table above (--features simd-x86-sse41) |
marker_bench |
ar_detect_marker + pose — barcode/template marker pipeline |
feature_map_bench |
ar2_gen_feature_map — NFT marker generation (--features log-helpers) |
pyramid_bench |
Box-filter pyramid downsample (kept-but-unused reference, #203) |
gaussian_pyramid_bench |
Gaussian scale-space pyramid build (#200/#201/#207) |
kpm_bench |
KpmHandle::kpm_matching — KPM/NFT detection (#225; see below) |
- Tooling: Criterion.rs
0.8 - Target OS: Windows
- Target Architecture: x86_64 (SSE4.1)
- Frame Size: 640x480 (typical AR video resolution)
- Toolchain:
rustc 1.94.0 (stable) - SIMD activation: SSE4.1 intrinsics are gated by
#[cfg(target_feature = "sse4.1")]— setRUSTFLAGS="-C target-feature=+sse4.1"(ortarget-cpu=native) when runningsimd_benchto exercise the SIMD paths.
Issue #142's acceptance criterion calls for the pure-Rust NFT pipeline
to run within 20% of the C++ backend on pinball-demo. marker_bench
measures ar_detect_marker (barcode/template marker detection), which
never touches the FreakMatcher and therefore can't distinguish the
pure-Rust backend from the C++ FFI one — hence the dedicated
kpm_bench.rs below, added in #225.
Times a single KpmHandle::kpm_matching query — the per-frame NFT
detection path — using the pure-Rust RustFreakMatcher backend.
| Measures | one KpmHandle::kpm_matching call (detection only) |
| Reference marker | pinball.fset3, assigned to page 0 |
| Query image | pinball-demo.jpg — 2000×1500, converted to luma |
| Fixtures | crates/core/examples/Data/ (shared with the simple_nft example and the KPM regression tests) |
| Criterion config | sample_size = 10, 15 s measurement — a full query is heavy |
cargo bench -p webarkitlib-rs --bench kpm_benchSetup — reference-data load and handle construction — happens once
outside the measured loop, so only kpm_matching is timed.
Baseline (release build, x86_64):
| Backend | Query time (median) |
|---|---|
Rust (RustFreakMatcher) |
~0.30 s |
To produce the C++ side of the #142 within-20% comparison, build with
--features ffi-backend and swap RustFreakMatcher for
CppFreakMatcher in the bench. Wall-clock numbers are
hardware-dependent, so treat the committed figure as an
order-of-magnitude reference rather than a hard CI gate.
Beyond wall-clock timing, the Rust and C++ backends agree on the meaningful outputs across several test suites:
| Test | What it asserts | Status post-#170 |
|---|---|---|
test_dual_mode_no_divergence_on_pinball |
M9 #152 tier-2: max_corner_displacement < 2.0 px between C++ and Rust homographies on found.jpg/img.jpg |
✅ zero divergences |
absolute_corner_error (#166 Track A) |
Each backend's max corner-error against hand-annotated ground truth stays within baseline + 3.5 px epsilon | ✅ green; Rust is more accurate than C++ on pinball-demo (Rust 5.27 px vs C++ 18.79 px) |
cross_stack_parity (jsartoolkitNFT#584 Track 2) |
C++ FFI and Rust pose agree with jsartoolkitNFT-Node within rot 0.08 / trans 10 mm | ✅ green |
kpm_regression::test_full_pipeline_pose |
Linux C++ pose matches committed numerical baseline to 1e-2 | ✅ green |
Taken together: Rust meets parity by every quality metric we measure,
and kpm_bench now provides the wall-clock baseline needed to catch
performance regressions in the FreakMatcher pipeline. The formal
within-20% comparison against C++ remains a manual, opt-in run
(--features ffi-backend) rather than a CI gate, per #142: "If
slower, open a follow-up performance issue rather than blocking this
PR."