#![deny(unsafe_code)]
mod alloc;
pub use alloc::SmallCache;
#[doc(hidden)]
pub use alloc::small_cache_simulate_numa;
pub fn tune_allocator() {
#[cfg(all(target_os = "linux", target_env = "gnu"))]
{
let (cpu_node, l2_share) = topology::detect();
alloc::configure(&cpu_node, l2_share);
const MMAP_THRESHOLD: libc::c_int = 256 * 1024;
const ARENA_MAX: libc::c_int = 4;
#[allow(unsafe_code)]
unsafe {
libc::mallopt(libc::M_MMAP_THRESHOLD, MMAP_THRESHOLD);
libc::mallopt(libc::M_ARENA_MAX, ARENA_MAX);
}
}
}
pub fn trim_memory() {
#[cfg(all(target_os = "linux", target_env = "gnu"))]
{
#[allow(unsafe_code)]
unsafe {
libc::malloc_trim(0);
}
}
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
mod topology {
use std::fs;
const SYS: &str = "/sys/devices/system";
pub(crate) fn detect() -> (Vec<(usize, usize)>, Option<usize>) {
let mut cpu_node = Vec::new();
let nodes = read(&format!("{SYS}/node/online")).map_or_else(Vec::new, |s| cpu_list(&s));
if nodes.len() > 1 {
for node in nodes {
if let Some(cpus) = read(&format!("{SYS}/node/node{node}/cpulist")) {
cpu_node.extend(cpu_list(&cpus).into_iter().map(|cpu| (cpu, node)));
}
}
}
(cpu_node, l2_share())
}
fn l2_share() -> Option<usize> {
for index in 0.. {
let dir = format!("{SYS}/cpu/cpu0/cache/index{index}");
let level = read(&format!("{dir}/level"))?;
if level.trim() != "2" {
continue;
}
let size = size(&read(&format!("{dir}/size"))?)?;
let sharing = read(&format!("{dir}/shared_cpu_list"))
.map_or(1, |s| cpu_list(&s).len())
.max(1);
return Some(size / sharing);
}
None
}
fn read(path: &str) -> Option<String> {
fs::read_to_string(path).ok()
}
pub(crate) fn size(s: &str) -> Option<usize> {
let s = s.trim();
let (digits, unit) = match s.as_bytes().last()? {
b'K' => (&s[..s.len() - 1], 1 << 10),
b'M' => (&s[..s.len() - 1], 1 << 20),
b'G' => (&s[..s.len() - 1], 1 << 30),
_ => (s, 1),
};
digits.parse::<usize>().ok().map(|n| n * unit)
}
pub(crate) fn cpu_list(s: &str) -> Vec<usize> {
let mut out = Vec::new();
for part in s.trim().split(',').filter(|p| !p.is_empty()) {
let mut ends = part.splitn(2, '-').map(|n| n.trim().parse::<usize>());
match (ends.next(), ends.next()) {
(Some(Ok(a)), None) => out.push(a),
(Some(Ok(a)), Some(Ok(b))) if a <= b => out.extend(a..=b),
_ => {}
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sysfs_lists_and_sizes_parse() {
assert_eq!(cpu_list("0-3,8,10-11\n"), vec![0, 1, 2, 3, 8, 10, 11]);
assert_eq!(cpu_list("0"), vec![0]);
assert_eq!(cpu_list(""), Vec::<usize>::new());
assert_eq!(size("1024K\n"), Some(1 << 20));
assert_eq!(size("2M"), Some(2 << 20));
assert_eq!(size("512"), Some(512));
assert_eq!(size("x"), None);
}
#[test]
fn this_machine_reports_an_l2_share() {
let (_, l2) = detect();
if std::path::Path::new("/sys/devices/system/cpu/cpu0/cache/index2").exists() {
assert!(l2.is_some_and(|b| b >= 4096), "{l2:?}");
}
}
}
}