#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Counters {
pub allocation_count: usize,
pub total_allocation_count: u64,
pub allocated_bytes: usize,
pub total_allocated_bytes: u64,
pub available_bytes: usize,
pub fragment_count: usize,
pub heap_count: usize,
pub total_heap_count: u64,
pub claimed_bytes: usize,
pub total_claimed_bytes: u64,
}
impl Counters {
#[inline]
pub(crate) const fn new() -> Self {
Self {
allocation_count: 0,
total_allocation_count: 0,
allocated_bytes: 0,
total_allocated_bytes: 0,
available_bytes: 0,
fragment_count: 0,
heap_count: 0,
total_heap_count: 0,
claimed_bytes: 0,
total_claimed_bytes: 0,
}
}
#[inline]
pub const fn overhead_bytes(&self) -> usize {
self.claimed_bytes - self.available_bytes - self.allocated_bytes
}
#[inline]
pub const fn total_freed_bytes(&self) -> u64 {
self.total_allocated_bytes - self.allocated_bytes as u64
}
#[inline]
pub const fn total_released_bytes(&self) -> u64 {
self.total_claimed_bytes - self.claimed_bytes as u64
}
#[inline]
pub(crate) fn account_register_gap(&mut self, size: usize) {
self.available_bytes += size;
self.fragment_count += 1;
}
#[inline]
pub(crate) fn account_deregister_gap(&mut self, size: usize) {
self.available_bytes -= size;
self.fragment_count -= 1;
}
#[inline]
pub(crate) fn account_alloc(&mut self, alloc_size: usize) {
self.allocation_count += 1;
self.allocated_bytes += alloc_size;
self.total_allocation_count += 1;
self.total_allocated_bytes += alloc_size as u64;
}
#[inline]
pub(crate) fn account_dealloc(&mut self, alloc_size: usize) {
self.allocation_count -= 1;
self.allocated_bytes -= alloc_size;
}
#[inline]
pub(crate) fn account_grow_in_place(&mut self, old_alloc_size: usize, new_alloc_size: usize) {
self.allocated_bytes += new_alloc_size - old_alloc_size;
self.total_allocated_bytes += (new_alloc_size - old_alloc_size) as u64;
}
#[inline]
pub(crate) fn account_shrink_in_place(&mut self, old_alloc_size: usize, new_alloc_size: usize) {
self.allocated_bytes -= old_alloc_size - new_alloc_size;
self.total_allocated_bytes -= (old_alloc_size - new_alloc_size) as u64;
}
#[inline]
pub(crate) fn account_claim(&mut self, claimed_size: usize) {
self.heap_count += 1;
self.claimed_bytes += claimed_size;
self.total_heap_count += 1;
self.total_claimed_bytes += claimed_size as u64;
}
#[inline]
pub(crate) fn account_append(&mut self, old_end: *mut u8, new_end: *mut u8) {
self.claimed_bytes += new_end as usize - old_end as usize;
self.total_claimed_bytes += (new_end as usize - old_end as usize) as u64;
}
#[inline]
pub(crate) fn account_truncate(
&mut self,
old_end: *mut u8,
new_end: *mut u8,
deleted_heap: bool,
) {
if deleted_heap {
self.heap_count -= 1;
self.claimed_bytes -= core::mem::size_of::<super::tag::Tag>();
}
self.claimed_bytes -= old_end as usize - new_end as usize;
}
}
impl core::fmt::Display for Counters {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
r#"Stat | Current Total | Accumulative Total
---------------------|---------------------|--------------------
# of Allocations | {:>19} | {:>19}
# of Allocated Bytes | {:>19} | {:>19}
# of Available Bytes | {:>19} | N/A
# of Overhead Bytes | {:>19} | N/A
# of Claimed Bytes | {:>19} | {:>19}
# of Heaps | {:>19} | {:>19}
# of Fragments | {:>19} | N/A"#,
self.allocation_count,
self.total_allocation_count,
self.allocated_bytes,
self.total_allocated_bytes,
self.available_bytes,
self.overhead_bytes(),
self.claimed_bytes,
self.total_claimed_bytes,
self.heap_count,
self.total_heap_count,
self.fragment_count,
)
}
}
impl<S: crate::source::Source, B: super::Binning> super::Talc<S, B> {
pub fn counters(&self) -> &Counters {
&self.counters
}
}
#[cfg(test)]
mod tests {
use ::core::alloc::Layout;
use core::ptr::null_mut;
use crate::base::CHUNK_UNIT;
use crate::base::binning::Binning;
use crate::source::Manual;
#[test]
fn test_claim_alloc_free_truncate() {
fn test_claim_alloc_free_truncate_inner<B: Binning>() {
let mut arena = [0u8; 1000000];
let mut talc = crate::base::Talc::<_, B>::new(Manual);
let mem_base = arena.as_mut_ptr().wrapping_add(99);
let mem_size = 10001;
let end = unsafe { talc.claim(mem_base, mem_size) }.unwrap();
let pre_alloc_claimed_bytes = talc.counters().claimed_bytes;
let pre_alloc_avl_bytes = talc.counters().available_bytes;
assert!(pre_alloc_claimed_bytes <= mem_size);
assert!(pre_alloc_claimed_bytes > mem_size - CHUNK_UNIT * 2);
assert_eq!(pre_alloc_claimed_bytes, talc.counters().total_claimed_bytes as _);
let max_meta_overhead = crate::min_first_heap_size::<B>();
assert!(pre_alloc_claimed_bytes - max_meta_overhead <= pre_alloc_avl_bytes);
assert_eq!(talc.counters().allocated_bytes, 0);
assert_eq!(talc.counters().total_allocated_bytes, 0);
assert_eq!(talc.counters().allocation_count, 0);
assert_eq!(talc.counters().total_allocation_count, 0);
assert_eq!(talc.counters().fragment_count, 1);
assert_eq!(
talc.counters().overhead_bytes(),
pre_alloc_claimed_bytes - pre_alloc_avl_bytes
);
let alloc_layout = Layout::new::<[u8; 3]>();
let alloc = unsafe { talc.allocate(alloc_layout).unwrap() };
let allocation_chunk_bytes =
crate::base::chunk::required_chunk_size(alloc_layout.size());
assert_eq!(talc.counters().claimed_bytes, pre_alloc_claimed_bytes);
assert_eq!(
talc.counters().available_bytes,
pre_alloc_avl_bytes - allocation_chunk_bytes
);
assert_eq!(talc.counters().allocated_bytes, alloc_layout.size());
assert_eq!(talc.counters().total_allocated_bytes, alloc_layout.size() as _);
assert_eq!(talc.counters().allocation_count, 1);
assert_eq!(talc.counters().total_allocation_count, 1);
assert_eq!(talc.counters().fragment_count, 1);
unsafe {
talc.deallocate(alloc.as_ptr(), alloc_layout);
}
assert_eq!(talc.counters().claimed_bytes, pre_alloc_claimed_bytes);
assert_eq!(talc.counters().total_claimed_bytes, pre_alloc_claimed_bytes as _);
assert_eq!(talc.counters().available_bytes, pre_alloc_avl_bytes);
assert_eq!(talc.counters().allocated_bytes, 0);
assert_eq!(talc.counters().total_allocated_bytes, alloc_layout.size() as _);
assert_eq!(talc.counters().allocation_count, 0);
assert_eq!(talc.counters().total_allocation_count, 1);
assert_eq!(talc.counters().fragment_count, 1);
let end = unsafe { talc.truncate(end, null_mut()) }.unwrap();
let extent = end.as_ptr() as usize
- crate::ptr_utils::align_up_by(mem_base, core::mem::size_of::<usize>()) as usize;
assert!(extent <= max_meta_overhead);
assert_eq!(talc.counters().claimed_bytes, extent);
assert_eq!(talc.counters().overhead_bytes(), extent);
assert_eq!(talc.counters().total_claimed_bytes, pre_alloc_claimed_bytes as _);
assert_eq!(talc.counters().available_bytes, 0);
assert_eq!(talc.counters().allocated_bytes, 0);
assert_eq!(talc.counters().total_allocated_bytes, alloc_layout.size() as _);
assert_eq!(talc.counters().allocation_count, 0);
assert_eq!(talc.counters().total_allocation_count, 1);
assert_eq!(talc.counters().fragment_count, 0);
assert_eq!(talc.counters().heap_count, 1);
assert_eq!(talc.counters().total_heap_count, 1);
}
for_many_talc_configurations!(test_claim_alloc_free_truncate_inner);
}
}