#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AutoCountPath {
SingleCached,
PipelineOneShot,
PipelineCached,
PoolParallel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AutoCountPlan {
pub path: AutoCountPath,
pub workers: usize,
pub chunk_size: usize,
}
impl AutoCountPlan {
pub const CACHE_MIN_BATCH: usize = 8;
pub const POOL_MIN_BATCH: usize = 4_096;
pub const POOL_MIN_CHUNK_PER_WORKER: usize = 1_024;
pub const POOL_MAX_WORKERS: usize = 16;
#[inline]
pub fn for_driver(batch_len: usize) -> Self {
if batch_len <= 1 {
return Self {
path: AutoCountPath::SingleCached,
workers: 1,
chunk_size: batch_len.max(1),
};
}
if batch_len < Self::CACHE_MIN_BATCH {
return Self {
path: AutoCountPath::PipelineOneShot,
workers: 1,
chunk_size: batch_len,
};
}
Self {
path: AutoCountPath::PipelineCached,
workers: 1,
chunk_size: batch_len,
}
}
#[inline]
pub fn for_pool(batch_len: usize, max_connections: usize, available_slots: usize) -> Self {
let driver_plan = Self::for_driver(batch_len);
if batch_len < Self::POOL_MIN_BATCH || max_connections < 2 || available_slots < 2 {
return driver_plan;
}
let by_chunk = batch_len / Self::POOL_MIN_CHUNK_PER_WORKER;
if by_chunk < 2 {
return driver_plan;
}
let workers = max_connections
.min(available_slots)
.min(Self::POOL_MAX_WORKERS)
.min(by_chunk);
if workers < 2 {
return driver_plan;
}
let chunk_size = batch_len.div_ceil(workers);
Self {
path: AutoCountPath::PoolParallel,
workers,
chunk_size,
}
}
}
#[cfg(test)]
mod tests {
use super::{AutoCountPath, AutoCountPlan};
#[test]
fn driver_auto_plan_resolves_expected_paths() {
let p0 = AutoCountPlan::for_driver(0);
assert_eq!(p0.path, AutoCountPath::SingleCached);
assert_eq!(p0.workers, 1);
let p1 = AutoCountPlan::for_driver(1);
assert_eq!(p1.path, AutoCountPath::SingleCached);
assert_eq!(p1.chunk_size, 1);
let p2 = AutoCountPlan::for_driver(2);
assert_eq!(p2.path, AutoCountPath::PipelineOneShot);
let p8 = AutoCountPlan::for_driver(8);
assert_eq!(p8.path, AutoCountPath::PipelineCached);
}
#[test]
fn pool_auto_plan_falls_back_when_parallel_not_worth_it() {
let p = AutoCountPlan::for_pool(512, 10, 10);
assert_eq!(p.path, AutoCountPath::PipelineCached);
assert_eq!(p.workers, 1);
}
#[test]
fn pool_auto_plan_uses_parallel_when_thresholds_met() {
let p = AutoCountPlan::for_pool(16_384, 10, 9);
assert_eq!(p.path, AutoCountPath::PoolParallel);
assert!(p.workers >= 2);
assert!(p.chunk_size >= AutoCountPlan::POOL_MIN_CHUNK_PER_WORKER);
}
}