use anyhow::{Context, Result};
use oxirs_core::RdfStore;
use std::time::Instant;
#[cfg(feature = "dhat-heap")]
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;
fn read_vm_rss_kib() -> Option<u64> {
let status = std::fs::read_to_string("/proc/self/status").ok()?;
for line in status.lines() {
if let Some(rest) = line.strip_prefix("VmRSS:") {
let kib: u64 = rest
.split_whitespace()
.next()
.and_then(|n| n.parse().ok())?;
return Some(kib);
}
}
None
}
fn read_vm_hwm_kib() -> Option<u64> {
let status = std::fs::read_to_string("/proc/self/status").ok()?;
for line in status.lines() {
if let Some(rest) = line.strip_prefix("VmHWM:") {
let kib: u64 = rest
.split_whitespace()
.next()
.and_then(|n| n.parse().ok())?;
return Some(kib);
}
}
None
}
fn mib(bytes: u64) -> f64 {
bytes as f64 / (1024.0 * 1024.0)
}
fn main() -> Result<()> {
#[cfg(feature = "dhat-heap")]
let _dhat = dhat::Profiler::new_heap();
let mut args = std::env::args().skip(1);
let dataset_dir = args
.next()
.context("usage: mem_profile_load <dataset_dir> [--shrink]")?;
let shrink = args.any(|a| a == "--shrink");
println!("== mem_profile_load ==");
println!("dataset dir : {dataset_dir}");
println!("shrink : {shrink}");
#[cfg(feature = "dhat-heap")]
println!("dhat-heap : ON (RSS figures from this run are NOT representative)");
let rss_before = read_vm_rss_kib().unwrap_or(0);
let start = Instant::now();
let store = RdfStore::open(&dataset_dir).context("open persistent store")?;
let load_elapsed = start.elapsed();
let len = store.len().context("len")?;
let rss_after_load = read_vm_rss_kib().unwrap_or(0);
let (size_est, rss_after_shrink, shrink_elapsed) = if shrink {
let s = Instant::now();
store.shrink_to_fit().context("shrink_to_fit")?;
let el = s.elapsed();
(
store.interned_size_estimate().unwrap_or(0),
read_vm_rss_kib().unwrap_or(0),
Some(el),
)
} else {
(
store.interned_size_estimate().unwrap_or(0),
rss_after_load,
None,
)
};
let peak_hwm = read_vm_hwm_kib().unwrap_or(0);
println!("---");
println!("quads loaded : {len}");
println!("load time : {:.2}s", load_elapsed.as_secs_f64());
if let Some(el) = shrink_elapsed {
println!("shrink time : {:.3}s", el.as_secs_f64());
}
println!(
"interned size_est : {} bytes ({:.1} MiB) = {:.0} bytes/quad",
size_est,
mib(size_est as u64),
if len > 0 {
size_est as f64 / len as f64
} else {
0.0
}
);
println!("RSS before open : {:.1} MiB", mib(rss_before * 1024));
println!(
"RSS after load : {:.1} MiB ({:.0} bytes/quad)",
mib(rss_after_load * 1024),
if len > 0 {
(rss_after_load * 1024) as f64 / len as f64
} else {
0.0
}
);
if shrink {
println!(
"RSS after shrink : {:.1} MiB ({:.0} bytes/quad)",
mib(rss_after_shrink * 1024),
if len > 0 {
(rss_after_shrink * 1024) as f64 / len as f64
} else {
0.0
}
);
}
println!("peak RSS (VmHWM) : {:.1} MiB", mib(peak_hwm * 1024));
#[cfg(feature = "dhat-heap")]
{
let stats = dhat::HeapStats::get();
println!("---dhat heap---");
println!(
"dhat max heap bytes : {} ({:.1} MiB) in {} blocks",
stats.max_bytes,
mib(stats.max_bytes as u64),
stats.max_blocks
);
println!(
"dhat total alloc : {} bytes over {} allocations",
stats.total_bytes, stats.total_blocks
);
}
std::hint::black_box(&store);
Ok(())
}