Benchmark suite for strided-rs based on the einsum benchmark (168 standardized einsum problems across 7 categories).
This repository provides:
- A Python pipeline to extract einsum benchmark metadata (shapes, dtypes, contraction paths) into portable JSON
- A Rust benchmark runner using strided-opteinsum
- A Julia benchmark runner using OMEinsum.jl
Only metadata is stored — tensors are generated at benchmark time (zero-filled), keeping the repo lightweight.
See tensor4all/strided-rs#63 for the full design discussion.
- Benchmark index: entry point for all published benchmark result pages.
- Einsum benchmarks: full-suite
strided-opteinsumversus OMEinsum.jl results and focused einsum case studies. - Strided benchmarks: kernel-level
comparisons for
naive,strided-rs, and HPTT where directly comparable.
strided-rs-benchmark-suite/
benchmarks/
README.md # Index for benchmark result pages
einsum_benchmarks/ # Full-suite einsum results and TN light 415 late-step case study
strided_benchmarks/ # Strided kernel benchmarks and comparisons
src/
main.rs # Rust benchmark runner (strided-opteinsum)
main.jl # Julia benchmark runner (OMEinsum.jl)
scripts/
run_all.sh # Run all benchmarks (configurable thread count)
generate_dataset.py # Filter & export benchmark instances as JSON
convert_tensornetwork.py # Convert TensorNetworkBenchmarks format to strided-rs JSON
create_lightweight_instance.py # Extract BFS-connected subgraph for lighter tensor network
format_results.py # Parse logs and output markdown tables
data/
instances/ # Exported JSON metadata (one file per instance)
results/ # Benchmark logs and markdown results
Cargo.toml # Rust project
Project.toml # Julia project
pyproject.toml # Python project
Requires Python 3.12+ and uv.
uv syncRequires a local clone of strided-rs at ../strided-rs.
Two GEMM backends are supported (mutually exclusive):
- faer (default) — pure Rust GEMM via faer
- blas — links to OpenBLAS (>= 0.3.29 recommended)
OpenBLAS setup:
- macOS:
brew install openblas - Ubuntu: build from source (system
libopenblas-devon Ubuntu 20.04 is 0.3.8 — too old for fair benchmarks)
# Build OpenBLAS 0.3.29 from source (one-time)
curl -L -o /tmp/OpenBLAS-0.3.29.tar.gz \
https://github.com/OpenMathLib/OpenBLAS/releases/download/v0.3.29/OpenBLAS-0.3.29.tar.gz
cd /tmp && tar xf OpenBLAS-0.3.29.tar.gz && cd OpenBLAS-0.3.29
make -j$(nproc) USE_OPENMP=0 NO_LAPACK=1
make PREFIX=$HOME/opt/openblas-0.3.29 install
# Set environment for building/running
export OPENBLAS_LIB_DIR=$HOME/opt/openblas-0.3.29/lib
export LD_LIBRARY_PATH=$HOME/opt/openblas-0.3.29/lib:$LD_LIBRARY_PATHcargo build --release # faer (default)
cargo build --release --no-default-features --features blas # OpenBLASjulia --project=. -e 'using Pkg; Pkg.instantiate()'uv run python scripts/generate_dataset.pyThis selects instances by category with laptop-scale criteria and saves JSON metadata to data/instances/. rnd_mixed_ instances are excluded (not yet supported by strided-rs).
Optional: Convert TensorNetworkBenchmarks format
To add the TensorNetworkBenchmarks tensor network (550 tensors, 2^33.2 complexity) as a strided-rs instance:
python scripts/convert_tensornetwork.pyThis requires the TensorNetworkBenchmarks repository cloned as a sibling directory:
cd .. && git clone https://github.com/TensorBFS/TensorNetworkBenchmarks.git && cd -Optional: Create a lightweight tensor network instance
The full tensornetwork_permutation_optimized instance (550 tensors) takes ~40 seconds. To create a lighter version (~5 seconds), extract a connected subgraph with an optimized contraction path:
uv run python scripts/create_lightweight_instance.py \
data/instances/tensornetwork_permutation_optimized.json \
data/instances/tensornetwork_permutation_light_415.json \
415The script finds a BFS-connected subset of the given size with minimal free indices, sets the output to scalar, and computes an optimized contraction path via opt_einsum.
Optional: Focus on the dominant late contraction steps (original steps 408/409)
For kernel-level profiling of the most expensive region in tensornetwork_permutation_light_415, this repo also includes:
data/instances/tensornetwork_permutation_focus_step409_316.json
This instance is extracted from the original 415-tensor contraction tree by taking the subtree that ends at original step 409. It preserves the expensive late-stage structure (original steps 408 and 409), while reducing total tree size from 415 to 316 tensors for faster iteration. Unlike the lightweight scalar instance, this focused instance has a non-scalar output (rank-18), because it represents an internal contraction state.
Use it when you want to benchmark or profile the bottleneck contraction kernels directly:
BENCH_INSTANCE=tensornetwork_permutation_focus_step409_316 ./scripts/run_all.sh 1Selection criteria (per category):
| Category | Prefix | log10[FLOPS] | log2[SIZE] | num_tensors | dtype |
|---|---|---|---|---|---|
| Language model | lm_ |
< 10 | < 25 | ≤ 100 | float64 or complex128 |
| Graphical model | gm_ |
< 10 | < 27 | ≤ 200 | float64 or complex128 |
| Structured | str_ |
< 11 | < 26 | ≤ 200 | float64 or complex128 |
./scripts/run_all.sh # 1 thread (default), canonical ids off
./scripts/run_all.sh 4 # 4 threads, canonical ids off
./scripts/run_all.sh 1 1 # 1 thread, canonical [lo, ro, batch] ids onRuns Rust (faer + blas) and Julia benchmarks.
- First argument sets
OMP_NUM_THREADS,RAYON_NUM_THREADS, andJULIA_NUM_THREADS. - Second argument sets
STRIDED_OPTEINSUM_CANONICAL_BINARY_IDS(0or1). If OpenBLAS is not installed, the blas benchmark is skipped with a warning. Results are saved todata/results/. To run only one instance, see Run a single instance below.
Instance JSON files that fail to read or parse are skipped with a warning; the suite continues with the rest. Instances that trigger a backend error (e.g. duplicate axis labels in strided-opteinsum) are reported as SKIP in the table with the reason on stderr.
To run the benchmark for one instance only, set the environment variable BENCH_INSTANCE to the instance name. Useful for heavy instances (e.g. gm_queen5_5_3.wcsp, str_nw_mera_closed_120, tensornetwork_permutation_optimized), lighter tensor network tests (e.g. tensornetwork_permutation_light_415), or focused bottleneck testing (e.g. tensornetwork_permutation_focus_step409_316).
With the full script (Rust + Julia):
BENCH_INSTANCE=str_nw_mera_closed_120 ./scripts/run_all.sh 1
BENCH_INSTANCE=gm_queen5_5_3.wcsp ./scripts/run_all.sh 4
BENCH_INSTANCE=tensornetwork_permutation_light_415 ./scripts/run_all.sh 1 1
BENCH_INSTANCE=tensornetwork_permutation_focus_step409_316 ./scripts/run_all.sh 1Rust or Julia alone:
BENCH_INSTANCE=str_nw_mera_closed_120 cargo run --release
BENCH_INSTANCE=str_nw_mera_closed_120 julia --project=. src/main.jl- Instance name must match the
namefield in the JSON (i.e. the filename without.json). To list available names:ls data/instances/→ e.g.str_nw_mera_closed_120.json→ usestr_nw_mera_closed_120. - Both the Rust and Julia runners respect
BENCH_INSTANCE; if set, only that instance is loaded and run for every strategy and mode.
The following binary-only benchmark datasets were added to make backend differences easier to isolate:
bin_matmul_256.json(ij,jk->ik)- Dense matrix multiplication baseline.
bin_batched_matmul_b32_m64_n64_k64.json(bij,bjk->bik)- Batched GEMM behavior with moderate batch size.
bin_outer_product_4096.json(i,j->ij)- Outer-product path (sensitive to broadcast/pack overhead).
bin_elementwise_mul_2048x2048.json(ij,ij->ij)- Pure elementwise multiplication throughput.
These are intended for quick, reproducible profiling before running heavier LM/structured/network cases.
Example:
RAYON_NUM_THREADS=1 OMP_NUM_THREADS=1 \
BENCH_INSTANCE=bin_outer_product_4096 cargo run --releaseRust (strided-opteinsum, faer backend):
RAYON_NUM_THREADS=1 OMP_NUM_THREADS=1 cargo run --releaseRust (strided-opteinsum, blas backend):
RAYON_NUM_THREADS=1 OMP_NUM_THREADS=1 cargo run --release --no-default-features --features blasJulia (OMEinsum.jl):
OMP_NUM_THREADS=1 JULIA_NUM_THREADS=1 julia --project=. src/main.jlJulia benchmark modes:
- omeinsum_path — follows the same pre-computed contraction path as Rust (fair kernel-level comparison)
- omeinsum_opt — OMEinsum.jl with
optimize_codeandTreeSA()(optimizer-chosen path)
CPU flamegraph (requires cargo install flamegraph):
BENCH_INSTANCE=tensornetwork_permutation_light_415 RAYON_NUM_THREADS=1 OMP_NUM_THREADS=1 \
cargo flamegraph --profile release-with-debug -o flamegraph.svg --no-default-features --features faerOpens an interactive SVG showing where CPU time is spent. On macOS, uses xctrace; on Linux, uses perf.
Internal profiler (plan/bgemm/buffer stats):
BENCH_INSTANCE=tensornetwork_permutation_light_415 STRIDED_EINSUM2_PROFILE=1 \
RAYON_NUM_THREADS=1 OMP_NUM_THREADS=1 cargo run --release --no-default-features --features faerPrints strided-einsum2 dispatch stats to stderr for each strategy:
total_calls,direct_calls,packed_callsm_packed_hist,n_packed_hist,k_packed_hist(bucketed packed-path dimensions)
NumPy arrays are row-major (C order). strided-rs uses column-major (Fortran order). The conversion is metadata-only:
- Reverse each tensor's shape:
[M, K]->[K, M] - Reverse each operand's index labels:
"ij,jk->ik"->"ji,kj->ki"
Both the original (format_string, shapes) and converted (format_string_colmajor, shapes_colmajor) metadata are stored in each JSON file.
Run all benchmarks (Rust faer + Rust blas + Julia):
./scripts/run_all.sh # 1 thread, canonical ids off
./scripts/run_all.sh 4 # 4 threads, canonical ids off
./scripts/run_all.sh 1 1 # 1 thread, canonical ids onThis script:
- Sets
OMP_NUM_THREADS,RAYON_NUM_THREADS,JULIA_NUM_THREADSto the given thread count (default: 1) andSTRIDED_OPTEINSUM_CANONICAL_BINARY_IDS(default: 0) - Builds and runs the Rust benchmark with the faer backend
- Builds and runs the Rust benchmark with the blas (OpenBLAS) backend (skipped if OpenBLAS is not installed)
- Runs the Julia benchmark (
julia --project=. src/main.jl) - Formats results as a markdown table via
scripts/format_results.py - Saves all outputs to
data/results/with timestamps
To format existing log files into a markdown table:
uv run python scripts/format_results.py data/results/rust_*.log data/results/julia_*.logSee Benchmark index for the current published result pages. The root README intentionally keeps large result tables out of the front page.
- Einsum Benchmark — standardized einsum benchmark suite
- ti2-group/einsum_benchmark — Python package
- tensor4all/strided-rs — Rust tensor library
- OMEinsum.jl — Julia einsum library
- TensorNetworkBenchmarks — tensor network contraction benchmarks