debugging stats

This commit is contained in:
Heikki Linnakangas
2025-05-03 19:21:29 +03:00
parent fc743e284f
commit 42df3e5453
5 changed files with 86 additions and 16 deletions

View File

@@ -73,7 +73,8 @@ pub(crate) fn update_fn<'e, K: Key, V: Value, A: ArtAllocator<V>, F>(
let root_ref = NodeRef::from_root_ptr(root);
let this_value_fn = |arg: Option<&V>| value_fn_cell.take().unwrap()(arg);
let key_bytes = key.as_bytes();
if let Ok(()) = update_recurse(
match update_recurse(
key_bytes,
this_value_fn,
root_ref,
@@ -82,9 +83,20 @@ pub(crate) fn update_fn<'e, K: Key, V: Value, A: ArtAllocator<V>, F>(
0,
key_bytes,
) {
break;
Ok(()) => break,
Err(ArtError::ConcurrentUpdate) => continue, // retry
Err(ArtError::OutOfMemory) => {
panic!("todo: OOM: try to GC, propagate to caller");
},
Err(ArtError::GarbageQueueFull) => {
if guard.collect_garbage() {
continue;
}
// FIXME: This can happen if someone is holding back the epoch. We should
// wait for the epoch to advance
panic!("todo: GC queue is full and couldn't free up space");
},
}
// retry
}
}

View File

@@ -1,4 +1,4 @@
mod block;
pub mod block;
mod multislab;
mod slab;
pub mod r#static;
@@ -12,6 +12,7 @@ use crate::allocator::r#static::alloc_from_slice;
use spin;
use crate::ArtTreeStatistics;
use crate::Tree;
pub use crate::algorithm::node_ptr::{
NodeInternal4, NodeInternal16, NodeInternal48, NodeInternal256, NodeLeaf4, NodeLeaf16,
@@ -138,3 +139,12 @@ impl<'t, V: crate::Value> ArtAllocator<V> for ArtMultiSlabAllocator<'t, V> {
self.inner.dealloc_slab(7, ptr.cast())
}
}
impl<'t, V: crate::Value> ArtMultiSlabAllocator<'t, V> {
pub fn get_statistics(&self) -> ArtTreeStatistics {
ArtTreeStatistics {
blocks: self.inner.block_allocator.get_statistics(),
}
}
}

View File

@@ -149,4 +149,35 @@ impl<'t> BlockAllocator<'t> {
unsafe { (*block_ptr) = init };
*freelist_head = blockno;
}
// for debugging
pub(crate) fn get_statistics(&self) -> BlockAllocatorStats {
let mut num_free_blocks = 0;
let mut _prev_lock= None;
let head_lock = self.freelist_head.lock();
let mut next_blk = *head_lock;
let mut _head_lock = Some(head_lock);
while next_blk != INVALID_BLOCK {
let freelist_block = self.read_freelist_block(next_blk);
let lock = freelist_block.inner.lock();
num_free_blocks += lock.num_free_blocks;
next_blk = lock.next;
_prev_lock = Some(lock); // hold the lock until we've read the next block
_head_lock = None;
}
BlockAllocatorStats {
num_blocks: self.num_blocks,
num_initialized: self.num_initialized.load(Ordering::Relaxed),
num_free_blocks,
}
}
}
#[derive(Clone, Debug)]
pub struct BlockAllocatorStats {
pub num_blocks: u64,
pub num_initialized: u64,
pub num_free_blocks: u64,
}

View File

@@ -311,18 +311,6 @@ impl<'t, K: Key + Clone, V: Value, A: ArtAllocator<V>> TreeWriteAccess<'t, K, V,
phantom_key: PhantomData,
}
}
pub fn collect_garbage(&'t self) {
self.tree.epoch.advance();
self.tree.epoch.broadcast();
let cutoff_epoch = self.tree.epoch.get_oldest();
let mut garbage_queue = self.tree.garbage.lock();
while let Some(ptr) = garbage_queue.next_obsolete(cutoff_epoch) {
ptr.deallocate(self.allocator);
}
}
}
impl<'t, K: Key + Clone, V: Value> TreeReadAccess<'t, K, V> {
@@ -386,10 +374,37 @@ impl<'t, K: Key, V: Value, A: ArtAllocator<V>> TreeWriteGuard<'t, K, V, A> {
.lock()
.remember_obsolete_node(ptr, self.epoch_pin.epoch)
}
// returns true if something was free'd up
fn collect_garbage(&'t self) -> bool {
let mut result = false;
self.tree.epoch.advance();
self.tree.epoch.broadcast();
let cutoff_epoch = self.tree.epoch.get_oldest();
let mut garbage_queue = self.tree.garbage.lock();
while let Some(ptr) = garbage_queue.next_obsolete(cutoff_epoch) {
ptr.deallocate(self.allocator);
result = true;
}
result
}
}
// Debugging functions
impl<'t, K: Key, V: Value + Debug> TreeReadGuard<'t, K, V> {
pub fn dump(&mut self) {
algorithm::dump_tree(self.tree.root, &self.epoch_pin)
}
}
impl<'t, K: Key, V: Value + Debug> TreeWriteGuard<'t, K, V, ArtMultiSlabAllocator<'t, V>> {
pub fn get_statistics(&self) -> ArtTreeStatistics {
self.allocator.get_statistics()
}
}
#[derive(Clone, Debug)]
pub struct ArtTreeStatistics {
pub blocks: allocator::block::BlockAllocatorStats,
}

View File

@@ -51,6 +51,8 @@ fn test_inserts<K: Into<TestKey> + Copy>(keys: &[K]) {
let value = r.get(&(*k).into());
assert_eq!(value, Some(idx));
}
eprintln!("stats: {:?}", tree_writer.start_write().get_statistics());
}
#[test]