use std::convert::Infallible;
use std::num::NonZeroUsize;
use super::policy::ParallelOptions;
use super::pool::{run_ordered_pool, run_ordered_pool_with_local, DEFAULT_CHUNK};
use super::ParallelHashError;
#[must_use]
pub(crate) fn pool_worthwhile(len: usize, total_bytes: usize, options: ParallelOptions) -> bool {
if options.threads.get() <= 1 || len == 0 {
return false;
}
if let Some(threshold) = options.signed_entry_threshold {
if (len as i64) < threshold {
return false;
}
}
if options.min_items == 0 {
return true;
}
if len < options.min_items {
return false;
}
if let Some(min_bytes) = options.min_total_bytes {
if total_bytes < min_bytes {
return false;
}
}
true
}
fn worker_count(options: ParallelOptions, len: usize) -> usize {
options.threads.get().min(len).max(1)
}
#[must_use]
pub fn par_map<T, R, F>(items: &[T], options: ParallelOptions, total_bytes: usize, f: F) -> Vec<R>
where
T: Sync,
R: Send,
F: Fn(&T) -> R + Sync,
{
try_par_map::<T, R, Infallible, _>(items, options, total_bytes, |item| Ok(f(item)))
.unwrap_or_else(|e| match e {
ParallelHashError::Task(infallible) => match infallible {},
})
}
pub fn try_par_map<T, R, E, F>(
items: &[T],
options: ParallelOptions,
total_bytes: usize,
f: F,
) -> Result<Vec<R>, ParallelHashError<E>>
where
T: Sync,
R: Send,
E: Send + 'static,
F: Fn(&T) -> Result<R, E> + Sync,
{
let len = items.len();
if len == 0 {
return Ok(Vec::new());
}
let n_workers = worker_count(options, len);
if !pool_worthwhile(len, total_bytes, options) {
return items
.iter()
.map(&f)
.collect::<Result<Vec<R>, E>>()
.map_err(ParallelHashError::Task);
}
run_ordered_pool(items, n_workers, DEFAULT_CHUNK, f).map_err(ParallelHashError::Task)
}
pub fn try_par_map_force<T, R, E, F>(
items: &[T],
options: ParallelOptions,
f: F,
) -> Result<Vec<R>, ParallelHashError<E>>
where
T: Sync,
R: Send,
E: Send + 'static,
F: Fn(&T) -> Result<R, E> + Sync,
{
let len = items.len();
if len == 0 {
return Ok(Vec::new());
}
let n_workers = worker_count(options, len);
if n_workers <= 1 {
return items
.iter()
.map(&f)
.collect::<Result<Vec<R>, E>>()
.map_err(ParallelHashError::Task);
}
run_ordered_pool(items, n_workers, DEFAULT_CHUNK, f).map_err(ParallelHashError::Task)
}
pub fn try_par_map_with_local<T, R, E, S, Init, F>(
items: &[T],
options: ParallelOptions,
total_bytes: usize,
init: Init,
f: F,
) -> Result<Vec<R>, ParallelHashError<E>>
where
T: Sync,
R: Send,
E: Send + 'static,
S: Send,
Init: Fn() -> S + Sync,
F: Fn(&mut S, &T) -> Result<R, E> + Sync,
{
let len = items.len();
if len == 0 {
return Ok(Vec::new());
}
let n_workers = worker_count(options, len);
if !pool_worthwhile(len, total_bytes, options) {
let mut state = init();
return items
.iter()
.map(|item| f(&mut state, item))
.collect::<Result<Vec<R>, E>>()
.map_err(ParallelHashError::Task);
}
run_ordered_pool_with_local(items, n_workers, DEFAULT_CHUNK, init, f)
.map_err(ParallelHashError::Task)
}
#[must_use]
pub fn parallel_worthwhile(
item_count: usize,
total_bytes: usize,
threads: NonZeroUsize,
min_items: usize,
min_total_bytes: usize,
) -> bool {
pool_worthwhile(
item_count,
total_bytes,
ParallelOptions {
threads,
min_items,
min_total_bytes: Some(min_total_bytes),
signed_entry_threshold: None,
},
)
}