Performance Benchmarking suite¶
This document describes the reproducible benchmark suite for the dense, blockwise, and
optional CuPy execution paths exposed through the public frsutils namespace.
It does not change runtime approximation behavior. The goal is to make
performance and equivalence claims measurable instead of relying on informal
manual timing.
Benchmark entry point¶
benchmarks/benchmark_fuzzy_rough_execution.py
The benchmark script compares:
| Scenario | Meaning |
|---|---|
dense_numpy |
Historical dense NumPy full-matrix approximation path. |
blockwise_numpy |
Exact blockwise NumPy approximation path. |
blockwise_cupy |
Optional CuPy-backed blockwise path. For ITFRS/VQRS this may use GPU-resident approximation accumulators; for OWAFRS it remains non-GPU-resident by the OWAFRS non-GPU-resident decision. |
The script writes machine-readable JSON and CSV reports.
Synthetic dataset size control¶
For synthetic benchmarks, control the generated dataset size directly with
--synthetic-samples and --n-features:
python benchmarks/benchmark_fuzzy_rough_execution.py \
--models itfrs,vqrs \
--synthetic-samples 5000 \
--n-features 20 \
--block-sizes 512,1024 \
--scenarios blockwise_numpy,blockwise_cupy \
--skip-dense-reference \
--compare-blockwise-backends \
--repeats 3 \
--output-json benchmark_synthetic_large.json \
--output-csv benchmark_synthetic_large_cases.csv \
--output-comparison-csv benchmark_synthetic_large_comparison.csv
Use --sample-sizes instead of --synthetic-samples when several synthetic
sizes should be tested in one run:
python benchmarks/benchmark_fuzzy_rough_execution.py \
--models itfrs \
--sample-sizes 1000,2000,5000 \
--n-features 20 \
--block-sizes 512 \
--scenarios blockwise_numpy,blockwise_cupy \
--skip-dense-reference \
--compare-blockwise-backends \
--output-json benchmark_size_sweep.json
Basic benchmark command¶
python benchmarks/benchmark_fuzzy_rough_execution.py \
--models itfrs,vqrs,owafrs \
--sample-sizes 128,256,512 \
--n-features 8 \
--block-sizes 64,128 \
--scenarios dense_numpy,blockwise_numpy,blockwise_cupy \
--repeats 3 \
--output-json benchmark_results.json \
--output-csv benchmark_results.csv
CPU-only environments are supported. If backend="cupy" is requested but CuPy
or CUDA is unavailable, the corresponding rows are marked as skipped rather
than crashing the whole benchmark run.
For release correctness evidence, use
scripts/capture_cuda_validation.py in addition to this timing harness. The
capture script records CUDA/GPU versions and a fixed dense NumPy versus
blockwise CuPy parity matrix; benchmark timing rows remain workload-specific
observations and must not be treated as general speedup guarantees.
Paired NumPy/CuPy blockwise comparison¶
For larger in-memory datasets, use paired comparison mode. This runs blockwise NumPy and blockwise CuPy on the same dataset, model, and block size, then records both runtime and numerical differences:
python benchmarks/benchmark_fuzzy_rough_execution.py \
--models itfrs,vqrs \
--synthetic-samples 10000 \
--n-features 30 \
--block-sizes 512,1024 \
--scenarios blockwise_numpy,blockwise_cupy \
--skip-dense-reference \
--compare-blockwise-backends \
--comparison-backends numpy,cupy \
--repeats 3 \
--output-json benchmark_paired.json \
--output-csv benchmark_paired_cases.csv \
--output-comparison-csv benchmark_paired_comparison.csv
The JSON report contains a top-level comparisons list. The comparison CSV
contains one row per model + sample size + block size pair with fields such as:
reference_median_runtime_seconds,candidate_median_runtime_seconds,speedup_reference_over_candidate,max_abs_diff_lower,max_abs_diff_upper,max_abs_diff_boundary,max_abs_diff_positive_region.
With the default --comparison-backends numpy,cupy, the speedup field is:
NumPy median runtime / CuPy median runtime
Values greater than 1 mean the candidate backend, normally CuPy, was faster in that benchmark case.
Larger in-memory CSV benchmark mode¶
For CSV benchmark data that can be loaded into memory, compare only blockwise CPU and GPU execution and skip the dense NumPy reference:
python benchmarks/benchmark_fuzzy_rough_execution.py \
--models itfrs,vqrs \
--input-csv path/to/dataset.csv \
--target-column class \
--block-sizes 512,1024 \
--scenarios blockwise_numpy,blockwise_cupy \
--repeats 3 \
--skip-dense-reference \
--compare-blockwise-backends \
--output-json benchmark_large.json \
--output-csv benchmark_large_cases.csv \
--output-comparison-csv benchmark_large_comparison.csv
The CSV loader expects a header row, one target column, and numeric feature columns. The target column can be selected by name or by zero-based column index. NumPy input is also supported:
python benchmarks/benchmark_fuzzy_rough_execution.py \
--models itfrs,vqrs \
--input-npy-x X.npy \
--input-npy-y y.npy \
--block-sizes 512,1024 \
--scenarios blockwise_numpy,blockwise_cupy \
--skip-dense-reference \
--compare-blockwise-backends \
--output-json benchmark_large.json \
--output-comparison-csv benchmark_large_comparison.csv
When --skip-dense-reference is used, dense-reference error fields in the
case-level results rows are left empty. Paired comparison rows still compare
NumPy blockwise directly against CuPy blockwise.
Recorded fields¶
Each case-level benchmark row records:
- status:
success,skipped, orfailed, - model, scenario, sample count, feature count, and block size,
- requested backend and resolved backend,
- median/mean/min/max runtime over repeated runs,
- Python allocator peak memory measured by
tracemalloc, - numerical-equivalence errors against the dense NumPy reference when enabled,
- whether dense reference computation was enabled,
- public result metadata:
used_blockwise,used_gpu_similarity_blocks,used_gpu_approximation_accumulators.
Each paired comparison row records:
- reference and candidate backend aliases,
- reference and candidate median/mean runtimes,
- speedup ratio,
- max absolute differences for lower, upper, boundary, and positive-region arrays,
- GPU metadata flags for both sides.
python_peak_memory_bytes is useful as a lightweight smoke metric, but it does
not fully capture native NumPy/CuPy allocator memory. For rigorous memory claims, run the suite on a controlled machine and
supplement it with OS or GPU memory tooling.
Expected interpretation¶
The benchmark suite supports these comparisons:
dense NumPy
vs blockwise NumPy
vs optional CuPy-backed blockwise execution
For CuPy rows, interpret metadata carefully:
ITFRS/VQRS + blockwise_cupy:
used_gpu_similarity_blocks may be True
used_gpu_approximation_accumulators may be True
OWAFRS + blockwise_cupy:
used_gpu_similarity_blocks may be True
used_gpu_approximation_accumulators remains False
This reflects the current OWAFRS contract: similarity blocks may use CuPy, while OWA approximation accumulation remains CPU-resident.
Tests¶
The benchmark suite has lightweight contract tests:
tests/benchmarks/test_benchmark_suite_contract.py
The tests run tiny CPU-only benchmark cases, verify dense/blockwise numerical equivalence, verify paired backend comparison behavior, and confirm that JSON/CSV artifacts are generated.
Boundary¶
The benchmark suite provides a reproducible harness rather than universal performance numbers. Reported results should come from a stable target machine with fixed package versions and documented CPU/GPU settings.
Reference-study benchmark profile¶
The reproducible research artifact under
studies/fuzzy_rough_reference_study/ runs this harness with a fixed CPU-only
profile and commits JSON/CSV outputs together with real-dataset approximation
tables and environment metadata. See the
reference-study guide for the one-command workflow and
claim boundaries.