#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum EvictionPolicy {
Lifo,
#[default]
ClockPro,
}
use std::thread;
const DEFAULT_MIN_BUFFER_SIZE: usize = 1024 * 1024;
const fn calculate_default_tls_cache_size(num_cpus: usize) -> usize {
match num_cpus {
0..=2 => 2,
3..=4 => 4,
5..=8 => 6,
_ => 8,
}
}
fn calculate_num_shards(num_cpus: usize) -> usize {
let target = (num_cpus / 2).max(4);
next_power_of_2(target).clamp(4, 128)
}
const fn calculate_max_buffers_per_shard(num_cpus: usize) -> usize {
const BASE: usize = 16;
let scaling = match num_cpus {
0..16 => 1,
16..24 => 2,
24..32 => 3,
_ => 4,
};
BASE * scaling
}
#[inline]
const fn next_power_of_2(n: usize) -> usize {
if n == 0 {
return 1;
}
let mut power = 1;
while power < n {
power <<= 1;
}
power
}
#[derive(Debug, Clone, Default)]
pub struct Builder {
num_shards: Option<usize>,
tls_cache_size: Option<usize>,
max_buffers_per_shard: Option<usize>,
min_buffer_size: Option<usize>,
pinned_memory: Option<bool>,
eviction_policy: Option<EvictionPolicy>,
}
impl Builder {
pub fn min_buffer_size(mut self, size: usize) -> Self {
self.min_buffer_size = Some(size);
self
}
pub fn num_shards(mut self, count: usize) -> Self {
self.num_shards = Some(count);
self
}
pub fn tls_cache_size(mut self, size: usize) -> Self {
self.tls_cache_size = Some(size);
self
}
pub fn max_buffers_per_shard(mut self, count: usize) -> Self {
self.max_buffers_per_shard = Some(count);
self
}
pub fn pinned_memory(mut self, enabled: bool) -> Self {
self.pinned_memory = Some(enabled);
self
}
pub fn eviction_policy(mut self, policy: EvictionPolicy) -> Self {
self.eviction_policy = Some(policy);
self
}
pub fn build(self) -> crate::BufferPool {
let num_cpus = thread::available_parallelism().map(std::num::NonZero::get).unwrap_or(4);
let tls_cache_size = self
.tls_cache_size
.unwrap_or_else(|| calculate_default_tls_cache_size(num_cpus));
let max_buffers_per_shard = self
.max_buffers_per_shard
.unwrap_or_else(|| calculate_max_buffers_per_shard(num_cpus));
assert!(tls_cache_size > 0, "tls_cache_size must be greater than 0");
assert!(max_buffers_per_shard > 0, "max_buffers_per_shard must be greater than 0");
let config = PoolConfig {
num_shards: self.num_shards.map_or_else(
|| calculate_num_shards(num_cpus),
|n| {
let normalized = next_power_of_2(n).clamp(4, 128);
debug_assert!(normalized.is_power_of_two(), "num_shards must be power of 2");
normalized
},
),
tls_cache_size,
max_buffers_per_shard,
min_buffer_size: self.min_buffer_size.unwrap_or(DEFAULT_MIN_BUFFER_SIZE),
pinned_memory: self.pinned_memory.unwrap_or(false),
eviction_policy: self.eviction_policy.unwrap_or_default(),
};
crate::BufferPool::with_config(config)
}
}
#[derive(Debug, Clone)]
pub(crate) struct PoolConfig {
pub num_shards: usize,
pub tls_cache_size: usize,
pub max_buffers_per_shard: usize,
pub min_buffer_size: usize,
pub pinned_memory: bool,
pub eviction_policy: EvictionPolicy,
}
impl Default for PoolConfig {
fn default() -> Self {
let num_cpus = thread::available_parallelism().map(std::num::NonZero::get).unwrap_or(4);
Self {
num_shards: calculate_num_shards(num_cpus),
tls_cache_size: calculate_default_tls_cache_size(num_cpus),
max_buffers_per_shard: calculate_max_buffers_per_shard(num_cpus),
min_buffer_size: DEFAULT_MIN_BUFFER_SIZE,
pinned_memory: false,
eviction_policy: EvictionPolicy::default(),
}
}
}