use ostd::cpu::num_cpus;
use super::{BuddyOrder, BuddySet, MAX_LOCAL_BUDDY_ORDER, OnDemandGlobalLock, lesser_order_of};
use crate::chunk::split_to_order;
const CACHE_EXPECTED_PORTION: usize = 2;
fn cache_expected_size(global_size: usize) -> usize {
global_size / num_cpus() / CACHE_EXPECTED_PORTION
}
const CACHE_MINIMAL_PORTION: usize = 8;
fn cache_minimal_size(global_size: usize) -> usize {
cache_expected_size(global_size) / CACHE_MINIMAL_PORTION
}
const CACHE_MAXIMAL_MULTIPLIER: usize = 2;
fn cache_maximal_size(global_size: usize) -> usize {
cache_expected_size(global_size) * CACHE_MAXIMAL_MULTIPLIER
}
pub fn balance(local: &mut BuddySet<MAX_LOCAL_BUDDY_ORDER>, global: &mut OnDemandGlobalLock) {
let global_size = global.get_global_size();
let minimal_local_size = cache_minimal_size(global_size);
let expected_local_size = cache_expected_size(global_size);
let maximal_local_size = cache_maximal_size(global_size);
let local_size = local.total_size();
if local_size >= maximal_local_size {
if local_size == 0 {
return;
}
let expected_removal = local_size - expected_local_size;
let lesser_order = lesser_order_of(expected_removal);
balance_to(local, &mut *global.get(), lesser_order);
} else if local_size < minimal_local_size {
if global_size == 0 {
return;
}
let expected_allocation = expected_local_size - local_size;
let lesser_order = lesser_order_of(expected_allocation);
balance_to(&mut *global.get(), local, lesser_order);
}
}
fn balance_to<const MAX_ORDER1: BuddyOrder, const MAX_ORDER2: BuddyOrder>(
a: &mut BuddySet<MAX_ORDER1>,
b: &mut BuddySet<MAX_ORDER2>,
order: BuddyOrder,
) {
let allocated_from_a = a.alloc_chunk(order);
if let Some(addr) = allocated_from_a {
if order >= MAX_ORDER2 {
let inserted_order = MAX_ORDER2 - 1;
split_to_order(addr, order, inserted_order).for_each(|addr| {
b.insert_chunk(addr, inserted_order);
});
} else {
b.insert_chunk(addr, order);
}
} else {
if order > 1 {
balance_to(a, b, order - 1);
balance_to(a, b, order - 1);
}
}
}