use rayon::prelude::*;
use super::cid::Cid;
use super::error::{Error, Mutation};
use super::node::Node;
use super::store::Store;
use super::tree::Tree;
use super::batch::{BatchApplyResult, BatchWriter, BatchWriterConfig};
use super::rebalance;
use super::Prolly;
#[derive(Clone, Debug)]
pub struct ParallelConfig {
pub max_threads: usize,
pub parallelism_threshold: usize,
}
impl Default for ParallelConfig {
fn default() -> Self {
Self {
max_threads: 0, parallelism_threshold: 100,
}
}
}
impl ParallelConfig {
pub fn new(max_threads: usize, parallelism_threshold: usize) -> Self {
Self {
max_threads,
parallelism_threshold,
}
}
pub fn sequential() -> Self {
Self {
max_threads: 1,
parallelism_threshold: usize::MAX,
}
}
}
fn batch_writer_config(config: &ParallelConfig, mutation_count: usize) -> BatchWriterConfig {
let default_config = BatchWriterConfig::new();
let prefetch_parallelism = if mutation_count < config.parallelism_threshold {
1
} else if config.max_threads == 0 {
default_config.prefetch_parallelism
} else {
config.max_threads
};
default_config.with_prefetch_parallelism(prefetch_parallelism.max(1))
}
pub(crate) fn parallel_batch_with_stats<S: Store>(
prolly: &Prolly<S>,
tree: &Tree,
mutations: Vec<Mutation>,
config: &ParallelConfig,
) -> Result<BatchApplyResult, Error> {
let writer_config = batch_writer_config(config, mutations.len());
BatchWriter::with_config(writer_config).apply_batch_with_stats(prolly, tree, mutations)
}
pub trait ParallelRebalancer<S: Store> {
fn parallel_rebalance(
&self,
store: &S,
prolly: &Prolly<S>,
nodes: Vec<(Node, Vec<(Node, usize)>)>,
config: &ParallelConfig,
) -> Result<Vec<Cid>, Error>;
fn parallel_batch(
&self,
_store: &S,
prolly: &Prolly<S>,
tree: &Tree,
mutations: Vec<Mutation>,
config: &ParallelConfig,
) -> Result<Tree, Error>;
}
#[derive(Clone, Debug, Default)]
pub struct DefaultParallelRebalancer;
impl DefaultParallelRebalancer {
pub fn new() -> Self {
Self
}
fn sequential_rebalance<S: Store>(
&self,
prolly: &Prolly<S>,
nodes: Vec<(Node, Vec<(Node, usize)>)>,
) -> Result<Vec<Cid>, Error> {
nodes
.into_iter()
.map(|(node, ancestors)| rebalance::rebalance(prolly, node, &ancestors))
.collect()
}
}
impl<S: Store> ParallelRebalancer<S> for DefaultParallelRebalancer {
fn parallel_rebalance(
&self,
_store: &S,
prolly: &Prolly<S>,
nodes: Vec<(Node, Vec<(Node, usize)>)>,
config: &ParallelConfig,
) -> Result<Vec<Cid>, Error> {
if nodes.len() < config.parallelism_threshold {
return self.sequential_rebalance(prolly, nodes);
}
if config.max_threads > 0 {
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(config.max_threads)
.build()
.map_err(|e| {
Error::Store(Box::new(std::io::Error::other(format!(
"Failed to create thread pool: {}",
e
))))
})?;
pool.install(|| {
nodes
.into_par_iter()
.map(|(node, ancestors)| rebalance::rebalance(prolly, node, &ancestors))
.collect()
})
} else {
nodes
.into_par_iter()
.map(|(node, ancestors)| rebalance::rebalance(prolly, node, &ancestors))
.collect()
}
}
fn parallel_batch(
&self,
_store: &S,
prolly: &Prolly<S>,
tree: &Tree,
mutations: Vec<Mutation>,
config: &ParallelConfig,
) -> Result<Tree, Error> {
Ok(parallel_batch_with_stats(prolly, tree, mutations, config)?.tree)
}
}