Skip to content

Repository files navigation

Vector Initialization Performance Benchmarks

This project explores and compares a variety of parallel and serial strategies for initializing a large Vec<u64> in Rust. It measures how different concurrency models and parallelization frameworks (Tokio and Rayon) affect performance, scalability, and cache locality relative to a single-threaded baseline.

TL;DR:

  • For very simple initial value computation functions (constants, simple arithmetic), you need on the order of 10^6 (i.e. millions) of items before parallel initialization makes sense.
  • For more complex initial value computation functions or very large vectors, tokio_static_chunks or any of the rayon work-stealing algorithms perform in the same order of magnitude amount of time and are reasonable choices.

Overview

Each benchmark fills a vector of size N = 1_000, 100_000, or 1_000_000, with computed values derived from a compute_value function. There are two versions of compute_value, a "simple" version that performs a simple arithmetic opertation, and a "complex" version that combines data lookup, per-thread RNG, and arithmetic.

Summary of Benchmarked Techniques

# Benchmark Name Framework Parallelism Model Chunking Strategy Work Distribution Notes
1 serial_baseline None Single-threaded None Sequential Reference baseline; ideal cache locality, no parallelism
2 tokio_static_chunks Tokio Multi-threaded (blocking pool) Static, one chunk per CPU Deterministic Low overhead; excellent for uniform workloads
3 tokio_dynamic_chunks Tokio Multi-threaded (blocking pool) Dynamic, ~8× chunks per CPU Semi-dynamic Better load balance for uneven workloads
4 tokio_per_element * Tokio Fine-grained None (1 task per element) Fully dynamic Extreme stress test for scheduling overhead
5 tokio_auto_chunks Tokio Multi-threaded (blocking pool) Fixed ~10 000 elements per task Deterministic Practical balance between granularity and overhead
6 rayon_par_range Rayon Work stealing Implicit adaptive ranges Dynamic Automatically balanced, concise API
7 rayon_par_iter_mut Rayon Work stealing Automatic adaptive slices Dynamic Efficient preallocated parallel fill
8 rayon_par_chunks Rayon Work stealing Explicit fixed-size slices Semi-static Tunable chunk size; similar to static Tokio version

* The tokio_per_element variant is included for completeness but commented out by default due to its extremely high overhead.

Running the Benchmarks

Run all benchmarks:

cargo bench

Run a specific benchmark:

cargo bench -- tokio_static_chunks

Benchmark results will be written under target/criterion/, including interactive HTML reports.

Empirical Data

Results as measured on my laptop in microseconds (μs). Fastest result is bold, second fastest is italic.

Complex Initializer Simple Initializer
(Algorithm, N) 1000 100,000 1,000,000 10,000 100,000 1,000,000
serial_baseline 16.352 2212.4 18118 2.5622 25.553 584.00
tokio_static_chunks 44.443 455.49 4541.2 40.856 49.742 495.47
tokio_dynamic_chunks 385.63 604.27 4180.2 384.3 382.21 772.52
tokio_auto_chunks 6584.4 62748 101150 96166 95723 65704
rayon_par_range 49.734 488.79 4414.6 50.915 74042 300.62
rayon_par_iter_mut 48.962 574.33 5465.2 43.42 69.25 515.83
rayon_par_chunks 30.057 536.28 4986.5 21.361 40.97 540.25

Authorship, Attribution, and License

Copyright © 2025 Robert Jacobson. This software is distributed under the terms of the MIT license or the Apache 2.0 license at your preference.

About

Experiments with different ways to initialize a vector

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages