use chacha20::ChaCha12Rng;
use clap::{Parser, ValueEnum};
use rand::seq::index;
use rand::SeedableRng;
use std::hint::black_box;
use std::num::NonZeroUsize;
fn main() {
let cli = Cli::parse();
match cli.parallelism {
Parallelism::Serial => dispatch_serial(cli),
Parallelism::Paralight => {
#[cfg(not(feature = "default-thread-pool"))]
panic!("Please compile this example with the `default-thread-pool` feature to use `--parallelism=paralight`.");
#[cfg(feature = "default-thread-pool")]
dispatch_paralight(cli);
}
Parallelism::Rayon => {
#[cfg(not(feature = "rayon"))]
panic!("Please compile this example with the `rayon` feature to use `--parallelism=rayon`.");
#[cfg(feature = "rayon")]
dispatch_rayon(cli);
}
Parallelism::ParalightOnRayon => {
#[cfg(not(feature = "rayon"))]
panic!("Please compile this example with the `rayon` feature to use `--parallelism=paralight-on-rayon`.");
#[cfg(feature = "rayon")]
dispatch_paralight_on_rayon(cli);
}
}
}
fn dispatch_serial(cli: Cli) {
match (cli.scenario, cli.owned_input, cli.boxed_items) {
(Scenario::Sum, false, false) => {
let input = (0..cli.input_size).collect::<Vec<u64>>();
let sum = black_box(input).iter().sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Sum, true, false) => {
let input = (0..cli.input_size).collect::<Vec<u64>>();
let sum = black_box(input).into_iter().sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Sum, false, true) => {
let input = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let sum = black_box(input).iter().map(|x| **x).sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Sum, true, true) => {
let input = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let sum = black_box(input).into_iter().map(|x| *x).sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Add, false, false) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).collect::<Vec<u64>>();
let right = (0..cli.input_size).collect::<Vec<u64>>();
let output_slice = output.as_mut_slice();
let left_slice = left.as_slice();
let right_slice = right.as_slice();
black_box(left_slice)
.iter()
.zip(black_box(right_slice).iter())
.zip(black_box(output_slice.iter_mut()))
.for_each(|((a, b), out)| *out = *a + *b);
println!("added {} elements", black_box(output).len());
}
(Scenario::Add, true, false) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).collect::<Vec<u64>>();
let right = (0..cli.input_size).collect::<Vec<u64>>();
let output_slice = output.as_mut_slice();
black_box(left)
.into_iter()
.zip(black_box(right))
.zip(black_box(output_slice.iter_mut()))
.for_each(|((a, b), out)| *out = a + b);
println!("added {} elements", black_box(output).len());
}
(Scenario::Add, false, true) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let right = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let output_slice = output.as_mut_slice();
let left_slice = left.as_slice();
let right_slice = right.as_slice();
black_box(left_slice)
.iter()
.zip(black_box(right_slice).iter())
.zip(black_box(output_slice.iter_mut()))
.for_each(|((a, b), out)| *out = **a + **b);
println!("added {} elements", black_box(output).len());
}
(Scenario::Add, true, true) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let right = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let output_slice = output.as_mut_slice();
black_box(left)
.into_iter()
.zip(black_box(right))
.zip(black_box(output_slice.iter_mut()))
.for_each(|((a, b), out)| *out = *a + *b);
println!("added {} elements", black_box(output).len());
}
(Scenario::FindAny | Scenario::FindFirst, false, false) => {
let input = fill_needles(cli.input_size as usize, cli.density);
let input_slice = input.as_slice();
let found = black_box(input_slice).iter().find(|x| **x);
println!("found = {found:?}");
}
(Scenario::FindAny | Scenario::FindFirst, true, false) => {
let input = fill_needles(cli.input_size as usize, cli.density);
let found = black_box(input).into_iter().find(|x| *x);
println!("found = {found:?}");
}
(Scenario::FindAny | Scenario::FindFirst, false, true) => {
let input = fill_boxed_needles(cli.input_size as usize, cli.density);
let input_slice = input.as_slice();
let found = black_box(input_slice).iter().find(|x| ***x);
println!("found = {found:?}");
}
(Scenario::FindAny | Scenario::FindFirst, true, true) => {
let input = fill_boxed_needles(cli.input_size as usize, cli.density);
let found = black_box(input).into_iter().find(|x| **x);
println!("found = {found:?}");
}
}
}
#[cfg(feature = "default-thread-pool")]
fn dispatch_paralight(cli: Cli) {
use paralight::prelude::*;
let mut thread_pool = ThreadPoolBuilder {
num_threads: match cli.num_threads {
Some(num_threads) => ThreadCount::Count(num_threads),
None => ThreadCount::AvailableParallelism,
},
range_strategy: match cli.range_strategy {
RangeStrategyCli::Fixed => RangeStrategy::Fixed,
RangeStrategyCli::WorkStealing => RangeStrategy::WorkStealing,
},
cpu_pinning: CpuPinningPolicy::IfSupported,
}
.build();
dispatch_thread_pool(cli, &mut thread_pool);
}
#[cfg(feature = "rayon")]
fn dispatch_paralight_on_rayon(cli: Cli) {
use paralight::prelude::*;
let thread_pool = RayonThreadPool::new_global(
match cli.num_threads {
Some(num_threads) => ThreadCount::Count(num_threads),
None => ThreadCount::AvailableParallelism,
},
match cli.range_strategy {
RangeStrategyCli::Fixed => RangeStrategy::Fixed,
RangeStrategyCli::WorkStealing => RangeStrategy::WorkStealing,
},
);
dispatch_thread_pool(cli, &thread_pool);
}
#[cfg(any(feature = "rayon", feature = "default-thread-pool"))]
fn dispatch_thread_pool(cli: Cli, thread_pool: impl paralight::iter::GenericThreadPool) {
use paralight::prelude::*;
match (cli.scenario, cli.owned_input, cli.boxed_items) {
(Scenario::Sum, false, false) => {
let input = (0..cli.input_size).collect::<Vec<u64>>();
let sum = black_box(input)
.par_iter()
.with_thread_pool(thread_pool)
.sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Sum, true, false) => {
let input = (0..cli.input_size).collect::<Vec<u64>>();
let sum = black_box(input)
.into_par_iter()
.with_thread_pool(thread_pool)
.sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Sum, false, true) => {
let input = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let sum = black_box(input)
.par_iter()
.with_thread_pool(thread_pool)
.map(|x| **x)
.sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Sum, true, true) => {
let input = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let sum = black_box(input)
.into_par_iter()
.with_thread_pool(thread_pool)
.map(|x| *x)
.sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Add, false, false) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).collect::<Vec<u64>>();
let right = (0..cli.input_size).collect::<Vec<u64>>();
let output_slice = output.as_mut_slice();
let left_slice = left.as_slice();
let right_slice = right.as_slice();
(
black_box(output_slice.par_iter_mut()),
black_box(left_slice).par_iter(),
black_box(right_slice).par_iter(),
)
.zip_eq()
.with_thread_pool(thread_pool)
.for_each(|(out, a, b)| *out = *a + *b);
println!("added {} elements", black_box(output).len());
}
(Scenario::Add, true, false) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).collect::<Vec<u64>>();
let right = (0..cli.input_size).collect::<Vec<u64>>();
let output_slice = output.as_mut_slice();
(
black_box(output_slice.par_iter_mut()),
black_box(left).into_par_iter(),
black_box(right).into_par_iter(),
)
.zip_eq()
.with_thread_pool(thread_pool)
.for_each(|(out, a, b)| *out = a + b);
println!("added {} elements", black_box(output).len());
}
(Scenario::Add, false, true) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let right = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let output_slice = output.as_mut_slice();
let left_slice = left.as_slice();
let right_slice = right.as_slice();
(
black_box(output_slice.par_iter_mut()),
black_box(left_slice).par_iter(),
black_box(right_slice).par_iter(),
)
.zip_eq()
.with_thread_pool(thread_pool)
.for_each(|(out, a, b)| *out = **a + **b);
println!("added {} elements", black_box(output).len());
}
(Scenario::Add, true, true) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let right = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let output_slice = output.as_mut_slice();
(
black_box(output_slice.par_iter_mut()),
black_box(left).into_par_iter(),
black_box(right).into_par_iter(),
)
.zip_eq()
.with_thread_pool(thread_pool)
.for_each(|(out, a, b)| *out = *a + *b);
println!("added {} elements", black_box(output).len());
}
(Scenario::FindAny, false, false) => {
let input = fill_needles(cli.input_size as usize, cli.density);
let input_slice = input.as_slice();
let found = black_box(input_slice)
.par_iter()
.with_thread_pool(thread_pool)
.find_any(|x| **x);
println!("found = {found:?}");
}
(Scenario::FindAny, true, false) => {
let input = fill_needles(cli.input_size as usize, cli.density);
let found = black_box(input)
.into_par_iter()
.with_thread_pool(thread_pool)
.find_any(|x| *x);
println!("found = {found:?}");
}
(Scenario::FindAny, false, true) => {
let input = fill_boxed_needles(cli.input_size as usize, cli.density);
let input_slice = input.as_slice();
let found = black_box(input_slice)
.par_iter()
.with_thread_pool(thread_pool)
.find_any(|x| ***x);
println!("found = {found:?}");
}
(Scenario::FindAny, true, true) => {
let input = fill_boxed_needles(cli.input_size as usize, cli.density);
let found = black_box(input)
.into_par_iter()
.with_thread_pool(thread_pool)
.find_any(|x| **x);
println!("found = {found:?}");
}
(Scenario::FindFirst, false, false) => {
let input = fill_needles(cli.input_size as usize, cli.density);
let input_slice = input.as_slice();
let found = black_box(input_slice)
.par_iter()
.with_thread_pool(thread_pool)
.find_first(|x| **x);
println!("found = {found:?}");
}
(Scenario::FindFirst, true, false) => {
let input = fill_needles(cli.input_size as usize, cli.density);
let found = black_box(input)
.into_par_iter()
.with_thread_pool(thread_pool)
.find_first(|x| *x);
println!("found = {found:?}");
}
(Scenario::FindFirst, false, true) => {
let input = fill_boxed_needles(cli.input_size as usize, cli.density);
let input_slice = input.as_slice();
let found = black_box(input_slice)
.par_iter()
.with_thread_pool(thread_pool)
.find_first(|x| ***x);
println!("found = {found:?}");
}
(Scenario::FindFirst, true, true) => {
let input = fill_boxed_needles(cli.input_size as usize, cli.density);
let found = black_box(input)
.into_par_iter()
.with_thread_pool(thread_pool)
.find_first(|x| **x);
println!("found = {found:?}");
}
}
}
#[cfg(feature = "rayon")]
fn dispatch_rayon(cli: Cli) {
use rayon::iter::{
IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator,
IntoParallelRefMutIterator, ParallelIterator,
};
match (cli.scenario, cli.owned_input, cli.boxed_items) {
(Scenario::Sum, false, false) => {
let input = (0..cli.input_size).collect::<Vec<u64>>();
let sum = black_box(input).par_iter().sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Sum, true, false) => {
let input = (0..cli.input_size).collect::<Vec<u64>>();
let sum = black_box(input).into_par_iter().sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Sum, false, true) => {
let input = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let sum = black_box(input).par_iter().map(|x| **x).sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Sum, true, true) => {
let input = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let sum = black_box(input).into_par_iter().map(|x| *x).sum::<u64>();
println!("sum = {sum}");
}
(Scenario::Add, false, false) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).collect::<Vec<u64>>();
let right = (0..cli.input_size).collect::<Vec<u64>>();
let output_slice = output.as_mut_slice();
let left_slice = left.as_slice();
let right_slice = right.as_slice();
black_box(output_slice.par_iter_mut())
.zip(
black_box(left_slice)
.par_iter()
.zip(black_box(right_slice).par_iter()),
)
.for_each(|(out, (a, b))| *out = *a + *b);
println!("added {} elements", black_box(output).len());
}
(Scenario::Add, true, false) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).collect::<Vec<u64>>();
let right = (0..cli.input_size).collect::<Vec<u64>>();
let output_slice = output.as_mut_slice();
black_box(output_slice.par_iter_mut())
.zip(
black_box(left)
.into_par_iter()
.zip(black_box(right).into_par_iter()),
)
.for_each(|(out, (a, b))| *out = a + b);
println!("added {} elements", black_box(output).len());
}
(Scenario::Add, false, true) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let right = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let output_slice = output.as_mut_slice();
let left_slice = left.as_slice();
let right_slice = right.as_slice();
black_box(output_slice.par_iter_mut())
.zip(
black_box(left_slice)
.par_iter()
.zip(black_box(right_slice).par_iter()),
)
.for_each(|(out, (a, b))| *out = **a + **b);
println!("added {} elements", black_box(output).len());
}
(Scenario::Add, true, true) => {
let mut output = vec![0; cli.input_size as usize];
let left = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let right = (0..cli.input_size).map(Box::new).collect::<Vec<Box<u64>>>();
let output_slice = output.as_mut_slice();
black_box(output_slice.par_iter_mut())
.zip(
black_box(left)
.into_par_iter()
.zip(black_box(right).into_par_iter()),
)
.for_each(|(out, (a, b))| *out = *a + *b);
println!("added {} elements", black_box(output).len());
}
(Scenario::FindAny, false, false) => {
let input = fill_needles(cli.input_size as usize, cli.density);
let input_slice = input.as_slice();
let found = black_box(input_slice).par_iter().find_any(|x| **x);
println!("found = {found:?}");
}
(Scenario::FindAny, true, false) => {
let input = fill_needles(cli.input_size as usize, cli.density);
let found = black_box(input).into_par_iter().find_any(|x| *x);
println!("found = {found:?}");
}
(Scenario::FindAny, false, true) => {
let input = fill_boxed_needles(cli.input_size as usize, cli.density);
let input_slice = input.as_slice();
let found = black_box(input_slice).par_iter().find_any(|x| ***x);
println!("found = {found:?}");
}
(Scenario::FindAny, true, true) => {
let input = fill_boxed_needles(cli.input_size as usize, cli.density);
let found = black_box(input).into_par_iter().find_any(|x| **x);
println!("found = {found:?}");
}
(Scenario::FindFirst, false, false) => {
let input = fill_needles(cli.input_size as usize, cli.density);
let input_slice = input.as_slice();
let found = black_box(input_slice).par_iter().find_first(|x| **x);
println!("found = {found:?}");
}
(Scenario::FindFirst, true, false) => {
let input = fill_needles(cli.input_size as usize, cli.density);
let found = black_box(input).into_par_iter().find_first(|x| *x);
println!("found = {found:?}");
}
(Scenario::FindFirst, false, true) => {
let input = fill_boxed_needles(cli.input_size as usize, cli.density);
let input_slice = input.as_slice();
let found = black_box(input_slice).par_iter().find_first(|x| ***x);
println!("found = {found:?}");
}
(Scenario::FindFirst, true, true) => {
let input = fill_boxed_needles(cli.input_size as usize, cli.density);
let found = black_box(input).into_par_iter().find_first(|x| **x);
println!("found = {found:?}");
}
}
}
fn fill_needles(input_size: usize, density: usize) -> Vec<bool> {
let mut input = vec![false; input_size];
let mut rng = ChaCha12Rng::seed_from_u64(42);
let needles = index::sample(&mut rng, input_size, density);
for needle in needles {
input[needle] = true;
}
input
}
#[expect(clippy::vec_box)]
fn fill_boxed_needles(input_size: usize, density: usize) -> Vec<Box<bool>> {
let mut input = vec![Box::new(false); input_size];
let mut rng = ChaCha12Rng::seed_from_u64(42);
let needles = index::sample(&mut rng, input_size, density);
for needle in needles {
*input[needle] = true;
}
input
}
#[derive(Parser, Debug, PartialEq, Eq)]
#[command(version)]
struct Cli {
#[arg(long)]
num_threads: Option<NonZeroUsize>,
#[arg(long, value_enum)]
range_strategy: RangeStrategyCli,
#[arg(long, value_enum, default_value_t = Parallelism::Paralight)]
parallelism: Parallelism,
#[arg(long, value_enum)]
scenario: Scenario,
#[arg(long, default_value_t = 1_000_000)]
input_size: u64,
#[arg(long, default_value_t = false)]
owned_input: bool,
#[arg(long, default_value_t = false)]
boxed_items: bool,
#[arg(long, default_value_t = 1)]
density: usize,
}
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
enum RangeStrategyCli {
Fixed,
WorkStealing,
}
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
enum Scenario {
Sum,
Add,
FindAny,
FindFirst,
}
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
enum Parallelism {
Serial,
Paralight,
Rayon,
ParalightOnRayon,
}