pub(crate) mod allocator;
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
pub struct AllocationInfo {
pub count_total: u64,
pub count_current: i64,
pub count_max: u64,
pub bytes_total: u64,
pub bytes_current: i64,
pub bytes_max: u64,
}
impl std::ops::AddAssign for AllocationInfo {
fn add_assign(&mut self, other: Self) {
self.count_total += other.count_total;
self.count_current += other.count_current;
self.count_max += other.count_max;
self.bytes_total += other.bytes_total;
self.bytes_current += other.bytes_current;
self.bytes_max += other.bytes_max;
}
}
pub fn measure<F: FnOnce()>(run_while_measuring: F) -> AllocationInfo {
allocator::ALLOCATIONS.with(|info_stack| {
let mut info_stack = info_stack.borrow_mut();
info_stack.depth += 1;
assert!(
(info_stack.depth as usize) < allocator::MAX_DEPTH,
"Too deep allocation measuring nesting"
);
let depth = info_stack.depth;
info_stack.elements[depth as usize] = AllocationInfo::default();
});
run_while_measuring();
allocator::ALLOCATIONS.with(|info_stack| {
let mut info_stack = info_stack.borrow_mut();
let depth = info_stack.depth;
let popped = info_stack.elements[depth as usize];
info_stack.depth -= 1;
let depth = info_stack.depth as usize;
info_stack.elements[depth] += popped;
popped
})
}
pub fn opt_out<F: FnOnce()>(run_while_not_counting: F) {
allocator::DO_COUNT.with(|b| {
*b.borrow_mut() += 1;
run_while_not_counting();
*b.borrow_mut() -= 1;
});
}
#[test]
fn test_measure() {
let info = measure(|| {
});
assert_eq!(info.bytes_current, 0);
assert_eq!(info.bytes_total, 0);
assert_eq!(info.bytes_max, 0);
assert_eq!(info.count_current, 0);
assert_eq!(info.count_total, 0);
assert_eq!(info.count_max, 0);
let info = measure(|| {
{
let _a = std::hint::black_box(Box::new(1_u32));
}
{
let _b = std::hint::black_box(Box::new(1_u32));
}
});
assert_eq!(info.bytes_current, 0);
assert_eq!(info.bytes_total, 8);
assert_eq!(info.bytes_max, 4);
assert_eq!(info.count_current, 0);
assert_eq!(info.count_total, 2);
assert_eq!(info.count_max, 1);
let info = measure(|| {
{
let _a = std::hint::black_box(Box::new(1_u32));
}
let b = std::hint::black_box(Box::new(1_u32));
std::mem::forget(b);
});
assert_eq!(info.bytes_current, 4);
assert_eq!(info.bytes_total, 8);
assert_eq!(info.bytes_max, 4);
assert_eq!(info.count_current, 1);
assert_eq!(info.count_total, 2);
assert_eq!(info.count_max, 1);
let info = measure(|| {
let a = std::hint::black_box(Box::new(1_u32));
let b = std::hint::black_box(Box::new(1_u32));
let _c = std::hint::black_box(Box::new(*a + *b));
});
assert_eq!(info.bytes_current, 0);
assert_eq!(info.bytes_total, 12);
assert_eq!(info.bytes_max, 12);
assert_eq!(info.count_current, 0);
assert_eq!(info.count_total, 3);
assert_eq!(info.count_max, 3);
}
#[test]
fn test_opt_out() {
let allocations = measure(|| {
});
assert_eq!(allocations.count_total, 0);
let allocations = measure(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
opt_out(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
opt_out(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
});
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
assert_eq!(allocations.count_total, 3);
let info = measure(|| {
opt_out(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
});
assert_eq!(0, info.count_total);
}
#[test]
fn test_nested_counting() {
let info = measure(|| {
let _a = std::hint::black_box(Box::new(1_u32));
let info = measure(|| {
let _b = std::hint::black_box(Box::new(1_u32));
});
assert_eq!(info.bytes_current, 0);
assert_eq!(info.bytes_total, 4);
assert_eq!(info.bytes_max, 4);
assert_eq!(info.count_current, 0);
assert_eq!(info.count_total, 1);
assert_eq!(info.count_max, 1);
});
assert_eq!(info.bytes_current, 0);
assert_eq!(info.bytes_total, 8);
assert_eq!(info.bytes_max, 8);
assert_eq!(info.count_current, 0);
assert_eq!(info.count_total, 2);
assert_eq!(info.count_max, 2);
}