From d7dca404bd059b93e34b52e69d16b1d9e2a6ed28 Mon Sep 17 00:00:00 2001 From: Igor Malovitsa Date: Tue, 3 Jun 2025 13:28:21 -0500 Subject: [PATCH 1/3] added counters to zippers --- examples/arena_compact_tests/src/main.rs | 5 + src/arena_compact.rs | 23 ++- src/lib.rs | 2 + src/timed_span.rs | 179 +++++++++++++++++++++++ src/zipper.rs | 32 ++++ 5 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 src/timed_span.rs diff --git a/examples/arena_compact_tests/src/main.rs b/examples/arena_compact_tests/src/main.rs index 72360ea0..7bbffedb 100644 --- a/examples/arena_compact_tests/src/main.rs +++ b/examples/arena_compact_tests/src/main.rs @@ -29,6 +29,8 @@ fn arena_create() -> Result<(), std::io::Error> { println!("len {:.2?}", tree.get_data().len()); // pathmap::alloc_tracking::read().print(); // pathmap::alloc_tracking::reset(); + pathmap::timed_span::print_counters(); + pathmap::timed_span::reset_counters(); let start = Instant::now(); let mut zipper = tree.read_zipper(); @@ -38,6 +40,8 @@ fn arena_create() -> Result<(), std::io::Error> { assert!(zipper.descend_to_existing(path) == path.len()); // assert_eq!(zipper.path(), path); } + pathmap::timed_span::print_counters(); + println!("checked act in {:.2?}", start.elapsed()); let start = Instant::now(); let tree2 = ArenaCompactTree::from_zipper(tree.read_zipper(), |_v| 0); @@ -78,6 +82,7 @@ fn arena_dump() -> Result<(), std::io::Error> { assert!(zipper.descend_to_existing(path) == path.len()); // assert_eq!(zipper.path(), path); } + pathmap::timed_span::print_counters(); println!("checked act in {:.2?}", start.elapsed()); let start = Instant::now(); let tree2 = ArenaCompactTree::from_zipper(tree.read_zipper(), |_v| 0); diff --git a/src/arena_compact.rs b/src/arena_compact.rs index 3084c2fd..12fa43ab 100644 --- a/src/arena_compact.rs +++ b/src/arena_compact.rs @@ -79,6 +79,7 @@ use crate::{ morphisms::Catamorphism, utils::{BitMask, ByteMask, find_prefix_overlap}, + timed_span::timed_span, zipper::{ Zipper, ZipperValues, ZipperForking, ZipperAbsolutePath, ZipperIteration, ZipperMoving, ZipperPathBuffer, ZipperReadOnlyValues, @@ -1657,10 +1658,13 @@ impl<'tree, Storage> ZipperMoving for ACTZipper<'tree, Storage> where Storage: AsRef<[u8]> { /// Returns `true` if the zipper cannot ascend further, otherwise returns `false` - fn at_root(&self) -> bool { self.path.len() <= self.origin_depth } + fn at_root(&self) -> bool { + self.path.len() <= self.origin_depth + } /// Resets the zipper's focus back to the root fn reset(&mut self) { + timed_span!(Reset); // self.ascend(self.path.len() - self.origin_depth); self.path.truncate(self.origin_depth); self.cur_node = self.tree.get_node(self.stack[0].node_id).0; @@ -1675,6 +1679,7 @@ where Storage: AsRef<[u8]> /// /// WARNING: This is not a cheap method. It may have an order-N cost fn val_count(&self) -> usize { + timed_span!(ValueCount); let mut zipper = self.clone(); zipper.reset(); let mut count = 0; @@ -1692,6 +1697,7 @@ where Storage: AsRef<[u8]> /// Returns `true` if the zipper points to an existing path within the tree, otherwise `false`. The /// zipper's location will be updated, regardless of whether or not the path exists within the tree. fn descend_to>(&mut self, path: P) -> bool { + timed_span!(DescendTo); let path = path.as_ref(); let depth = path.len(); let descended = self.descend_to_existing(path); @@ -1711,6 +1717,7 @@ where Storage: AsRef<[u8]> /// existing path after this method returns, unless the method was called with the focus on a /// non-existent path. fn descend_to_existing>(&mut self, path: P) -> usize { + timed_span!(DescendToExisting); self.descend_cond(path.as_ref(), false) } @@ -1722,12 +1729,14 @@ where Storage: AsRef<[u8]> /// If the focus is already on a value, this method will descend to the *next* value along /// the path. fn descend_to_value>(&mut self, path: K) -> usize { + timed_span!(DescendToValue); self.descend_cond(path.as_ref(), true) } /// Moves the zipper one byte deeper into the trie. Identical in effect to [descend_to](Self::descend_to) /// with a 1-byte key argument fn descend_to_byte(&mut self, k: u8) -> bool { + timed_span!(DescendToByte); self.descend_to(&[k]) } @@ -1739,6 +1748,7 @@ where Storage: AsRef<[u8]> /// to the trie. This method should only be used as part of a directed traversal operation, but /// index-based paths may not be stored as locations within the trie. fn descend_indexed_branch(&mut self, idx: usize) -> bool { + timed_span!(DescendIndexedBranch); if self.invalid > 0 { return false; } @@ -1793,12 +1803,14 @@ where Storage: AsRef<[u8]> /// NOTE: This method should have identical behavior to passing `0` to [descend_indexed_branch](ZipperMoving::descend_indexed_branch), /// although with less overhead fn descend_first_byte(&mut self) -> bool { + timed_span!(DescendFirstByte); self.descend_indexed_branch(0) } /// Descends the zipper's focus until a branch or a value is encountered. Returns `true` if the focus /// moved otherwise returns `false` fn descend_until(&mut self) -> bool { + timed_span!(DescendUntil); self.trace_pos(); let mut descended = false; 'descend: while self.child_count() == 1 { @@ -1849,6 +1861,7 @@ where Storage: AsRef<[u8]> /// If the root is fewer than `n` steps from the zipper's position, then this method will stop at /// the root and return `false` fn ascend(&mut self, mut steps: usize) -> bool { + timed_span!(Ascend); self.trace_pos(); if !self.ascend_invalid(Some(steps)) { return false; @@ -1875,12 +1888,14 @@ where Storage: AsRef<[u8]> /// Ascends the zipper up a single byte. Equivalent to passing `1` to [ascend](Self::ascend) fn ascend_byte(&mut self) -> bool { + timed_span!(AscendByte); self.ascend(1) } /// Ascends the zipper to the nearest upstream branch point or value. Returns `true` if the zipper /// focus moved upwards, otherwise returns `false` if the zipper was already at the root fn ascend_until(&mut self) -> bool { + timed_span!(AscendUntil); self.ascend_to_branch(true) } @@ -1888,16 +1903,19 @@ where Storage: AsRef<[u8]> /// `true` if the zipper focus moved upwards, otherwise returns `false` if the zipper was already at the /// root fn ascend_until_branch(&mut self) -> bool { + timed_span!(AscendUntilBranch); self.ascend_to_branch(false) } #[inline] fn to_next_sibling_byte(&mut self) -> bool { + timed_span!(ToNextSiblingByte); self.to_sibling(true) } #[inline] fn to_prev_sibling_byte(&mut self) -> bool { + timed_span!(ToPrevSiblingByte); self.to_sibling(false) } @@ -1913,6 +1931,7 @@ where Storage: AsRef<[u8]> /// /// Returns a reference to the value or `None` if the zipper has encountered the root. fn to_next_val(&mut self) -> bool { + timed_span!(ToNextVal); while self.to_next_step() { if self.is_value() { return true; @@ -1932,6 +1951,7 @@ where Storage: AsRef<[u8]> /// /// See: [to_next_k_path](ZipperIteration::to_next_k_path) fn descend_first_k_path(&mut self, k: usize) -> bool { + timed_span!(DescendFirstKPath); for ii in 0..k { if !self.descend_first_byte() { self.ascend(ii); @@ -1953,6 +1973,7 @@ where Storage: AsRef<[u8]> /// /// See: [descend_first_k_path](ZipperIteration::descend_first_k_path) fn to_next_k_path(&mut self, k: usize) -> bool { + timed_span!(ToNextKPath); let mut depth = k; 'outer: loop { while depth > 0 && self.child_count() <= 1 { diff --git a/src/lib.rs b/src/lib.rs index 8f87c97a..b0494d4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,8 @@ pub mod fuzzer; #[cfg(feature = "counters")] pub mod counters; +pub mod timed_span; + pub mod serialization; pub mod path_serialization; pub mod tree_serialization; diff --git a/src/timed_span.rs b/src/timed_span.rs new file mode 100644 index 00000000..005a7e5d --- /dev/null +++ b/src/timed_span.rs @@ -0,0 +1,179 @@ +use std::arch::asm; +use std::sync::atomic::{AtomicU64, Ordering}; + +macro_rules! entries { + ( + pub enum $name:ident { + $($entry:ident $(= $value:expr)?),* + $(,)? + } + ) => { + #[derive(Clone, Copy)] + pub enum $name { + $($entry $(= $value)?),* + } + impl $name { + const ALL: &[$name] = &[$($name::$entry),*]; + const COUNT: usize = $name::ALL.len(); + pub fn to_str(&self) -> &'static str { + match self { + $( + $name::$entry => stringify!($entry) + ),* + } + } + } + + } +} + +entries!{ + pub enum Entries { + Reset, + ValueCount, + DescendTo, + DescendToExisting, + DescendToValue, + DescendToByte, + DescendIndexedBranch, + DescendFirstByte, + DescendUntil, + MoveToPath, + AscendByte, + Ascend, + ToNextSiblingByte, + ToPrevSiblingByte, + ToNextStep, + AscendUntil, + AscendUntilBranch, + ToNextVal, + DescendFirstKPath, + ToNextKPath, + ToNextGetValue, + ForkReadZipper, + } +} + +pub struct Counter { + pub count: AtomicU64, + pub cycles: AtomicU64, +} + +impl Counter { + const fn new() -> Self { + Self { + count: AtomicU64::new(0), + cycles: AtomicU64::new(0), + } + } +} + +pub static COUNTERS: [Counter; Entries::COUNT] = + [const { Counter::new() }; Entries::COUNT]; + +pub fn reset_counters() { + for counter in &COUNTERS { + counter.count.store(0, Ordering::Relaxed); + counter.cycles.store(0, Ordering::Relaxed); + } +} + +pub fn print_counters() { + println!("{:>20},Count,TSCDelta,TSCAverage", "Name"); + for &entry in Entries::ALL { + let counter = &COUNTERS[entry as usize]; + let count = counter.count.load(Ordering::Relaxed); + let cycles = counter.cycles.load(Ordering::Relaxed); + if count == 0 && cycles == 0 { continue; } + let average = cycles as f64 / count as f64; + println!("{:>20},{},{},{}", entry.to_str(), count, cycles, average); + } +} + +#[cfg(target_arch = "aarch64")] +#[inline] +fn read_cycle_counter() -> u64 { + let val: u64; + unsafe { + asm!( + "mrs {}, CNTVCT_EL0", + out(reg) val, + options(nostack, nomem) + ); + } + val +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[inline] +fn read_cycle_counter() -> u64 { + let lo: u32; + let hi: u32; + unsafe { + asm!( + "rdtsc", + out("eax") lo, + out("edx") hi, + options(nostack, nomem) + ); + } + ((hi as u64) << 32) | (lo as u64) +} + +pub struct TimedSpanGuard<'a> { + start: u64, + counter: &'a AtomicU64, +} + +impl<'a> TimedSpanGuard<'a> { + pub fn new(counter: &'a AtomicU64) -> Self { + Self { + start: read_cycle_counter(), + counter, + } + } +} + +impl<'a> Drop for TimedSpanGuard<'a> { + fn drop(&mut self) { + let end = read_cycle_counter(); + self.counter.fetch_add(end - self.start, Ordering::Relaxed); + } +} + +pub const ENABLED: bool = true; + +macro_rules! timed_span { + ($name:ident, $dst:path) => { + let _guard = if $crate::timed_span::ENABLED { + let index = $crate::timed_span::Entries::$name as usize; + let counter = &$dst[index]; + counter.count.fetch_add(1, core::sync::atomic::Ordering::Relaxed); + Some($crate::timed_span::TimedSpanGuard::new(&counter.cycles)) + } else { + None + }; + }; + ($name:ident) => { + $crate::timed_span::timed_span!($name, $crate::timed_span::COUNTERS) + }; +} + +pub(crate) use timed_span; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_timed_span() { + reset_counters(); + { + timed_span!(Reset); + for ii in 0..100_000 { + core::hint::black_box(ii); + } + } + print_counters(); + } +} diff --git a/src/zipper.rs b/src/zipper.rs index 471242d9..3f58989e 100644 --- a/src/zipper.rs +++ b/src/zipper.rs @@ -30,6 +30,7 @@ use maybe_dangling::MaybeDangling; use crate::utils::{ByteMask, find_prefix_overlap}; use crate::trie_node::*; use crate::trie_map::BytesTrieMap; +use crate::timed_span::timed_span; pub use crate::write_zipper::*; pub use crate::trie_ref::*; @@ -121,6 +122,7 @@ pub trait ZipperMoving: Zipper { /// Resets the zipper's focus back to its root fn reset(&mut self) { + timed_span!(Reset); while !self.at_root() { self.ascend_byte(); } @@ -139,6 +141,7 @@ pub trait ZipperMoving: Zipper { /// /// Returns the number of bytes shared between the old and new location and whether the new location exists in the trie fn move_to_path>(&mut self, path: K) -> (usize, bool) { + timed_span!(MoveToPath); let path = path.as_ref(); let p = self.path(); let overlap = find_prefix_overlap(path, p); @@ -165,6 +168,7 @@ pub trait ZipperMoving: Zipper { /// existing path after this method returns, unless the method was called with the focus on a /// non-existent path. fn descend_to_existing>(&mut self, k: K) -> usize { + timed_span!(DescendToExisting); let k = k.as_ref(); let mut i = 0; while i < k.len() { @@ -186,6 +190,7 @@ pub trait ZipperMoving: Zipper { /// the path. //GOAT. this default implementation could certainly be optimized fn descend_to_value>(&mut self, k: K) -> usize { + timed_span!(DescendToValue); let k = k.as_ref(); let mut i = 0; while i < k.len() { @@ -204,6 +209,7 @@ pub trait ZipperMoving: Zipper { /// Moves the zipper one byte deeper into the trie. Identical in effect to [descend_to](Self::descend_to) /// with a 1-byte key argument fn descend_to_byte(&mut self, k: u8) -> bool { + timed_span!(DescendToByte); self.descend_to(&[k]) } @@ -216,6 +222,7 @@ pub trait ZipperMoving: Zipper { /// to the trie. This method should only be used as part of a directed traversal operation, but /// index-based paths may not be stored as locations within the trie. fn descend_indexed_branch(&mut self, idx: usize) -> bool { + timed_span!(DescendIndexedBranch); let mask = self.child_mask(); let child_byte = match mask.indexed_bit::(idx) { Some(byte) => byte, @@ -233,12 +240,14 @@ pub trait ZipperMoving: Zipper { /// NOTE: This method should have identical behavior to passing `0` to [descend_indexed_branch](ZipperMoving::descend_indexed_branch), /// although with less overhead fn descend_first_byte(&mut self) -> bool { + timed_span!(DescendFirstByte); self.descend_indexed_branch(0) } /// Descends the zipper's focus until a branch or a value is encountered. Returns `true` if the focus /// moved otherwise returns `false` fn descend_until(&mut self) -> bool { + timed_span!(DescendUntil); let mut descended = false; while self.child_count() < 2 { if self.descend_first_byte() { @@ -258,6 +267,7 @@ pub trait ZipperMoving: Zipper { /// Ascends the zipper up a single byte. Equivalent to passing `1` to [ascend](Self::ascend) fn ascend_byte(&mut self) -> bool { + timed_span!(AscendByte); self.ascend(1) } @@ -316,6 +326,7 @@ pub trait ZipperMoving: Zipper { /// This method is equivalent to calling [ZipperMoving::ascend] with `1`, followed by [ZipperMoving::descend_indexed_branch] /// where the index passed is 1 more than the index of the current focus position. fn to_next_sibling_byte(&mut self) -> bool { + timed_span!(ToNextSiblingByte); let cur_byte = match self.path().last() { Some(byte) => *byte, None => return false @@ -346,6 +357,7 @@ pub trait ZipperMoving: Zipper { /// This method is equivalent to calling [Self::ascend] with `1`, followed by [Self::descend_indexed_branch] /// where the index passed is 1 less than the index of the current focus position. fn to_prev_sibling_byte(&mut self) -> bool { + timed_span!(ToPrevSiblingByte); let cur_byte = match self.path().last() { Some(byte) => *byte, None => return false @@ -373,6 +385,7 @@ pub trait ZipperMoving: Zipper { /// Returns `true` if the position of the zipper has moved, or `false` if the zipper has returned /// to the root fn to_next_step(&mut self) -> bool { + timed_span!(ToNextStep); //If we're at a leaf ascend until we're not and jump to the next sibling if self.child_count() == 0 { @@ -1234,6 +1247,7 @@ pub(crate) mod read_zipper_core { impl ZipperForking for ReadZipperCore<'_, '_, V> { type ReadZipperT<'a> = ReadZipperCore<'a, 'a, V> where Self: 'a; fn fork_read_zipper<'a>(&'a self) -> Self::ReadZipperT<'a> { + timed_span!(ForkReadZipper); let new_root_val = self.get_value(); let new_root_path = self.origin_path(); let new_root_key_offset = new_root_path.len() - self.node_key().len(); @@ -1263,6 +1277,7 @@ pub(crate) mod read_zipper_core { } fn reset(&mut self) { + timed_span!(Reset); self.ancestors.truncate(1); match self.ancestors.pop() { Some((node, _tok, _prefix_len)) => { @@ -1284,6 +1299,7 @@ pub(crate) mod read_zipper_core { } fn val_count(&self) -> usize { + timed_span!(ValueCount); if self.node_key().len() == 0 { val_count_below_root(self.focus_node.borrow()) + (self.is_value() as usize) } else { @@ -1296,6 +1312,7 @@ pub(crate) mod read_zipper_core { } } fn descend_to>(&mut self, k: K) -> bool { + timed_span!(DescendTo); let k = k.as_ref(); if k.len() == 0 { return self.path_exists() //Zero-length path is a no-op @@ -1313,6 +1330,7 @@ pub(crate) mod read_zipper_core { } fn descend_to_byte(&mut self, k: u8) -> bool { + timed_span!(DescendToByte); self.prepare_buffers(); debug_assert!(self.is_regularized()); @@ -1328,6 +1346,7 @@ pub(crate) mod read_zipper_core { } fn descend_indexed_branch(&mut self, child_idx: usize) -> bool { + timed_span!(DescendIndexedBranch); self.prepare_buffers(); debug_assert!(self.is_regularized()); @@ -1349,6 +1368,7 @@ pub(crate) mod read_zipper_core { } fn descend_first_byte(&mut self) -> bool { + timed_span!(DescendFirstByte); self.prepare_buffers(); debug_assert!(self.is_regularized()); let cur_tok = self.focus_node.iter_token_for_path(self.node_key()); @@ -1385,6 +1405,7 @@ pub(crate) mod read_zipper_core { } fn descend_until(&mut self) -> bool { + timed_span!(DescendUntil); debug_assert!(self.is_regularized()); let mut moved = false; while self.child_count() == 1 { @@ -1398,6 +1419,7 @@ pub(crate) mod read_zipper_core { } fn descend_to_existing>(&mut self, k: K) -> usize { + timed_span!(DescendToExisting); let mut k = k.as_ref(); if k.len() == 0 { return 0 //Zero-length path is a no-op @@ -1463,6 +1485,7 @@ pub(crate) mod read_zipper_core { // } fn to_next_sibling_byte(&mut self) -> bool { + timed_span!(ToNextSiblingByte); self.prepare_buffers(); if self.prefix_buf.len() == 0 { return false @@ -1518,10 +1541,12 @@ pub(crate) mod read_zipper_core { } fn to_prev_sibling_byte(&mut self) -> bool { + timed_span!(ToPrevSiblingByte); self.to_sibling(false) } fn ascend(&mut self, mut steps: usize) -> bool { + timed_span!(Ascend); debug_assert!(self.is_regularized()); while steps > 0 { if self.excess_key_len() == 0 { @@ -1545,6 +1570,7 @@ pub(crate) mod read_zipper_core { } fn ascend_byte(&mut self) -> bool { + timed_span!(AscendByte); debug_assert!(self.is_regularized()); if self.excess_key_len() == 0 { match self.ancestors.pop() { @@ -1564,6 +1590,7 @@ pub(crate) mod read_zipper_core { } fn ascend_until(&mut self) -> bool { + timed_span!(AscendUntil); debug_assert!(self.is_regularized()); if self.at_root() { return false; @@ -1580,6 +1607,7 @@ pub(crate) mod read_zipper_core { } fn ascend_until_branch(&mut self) -> bool { + timed_span!(AscendUntilBranch); debug_assert!(self.is_regularized()); if self.at_root() { return false; @@ -1737,9 +1765,11 @@ pub(crate) mod read_zipper_core { impl ZipperIteration for ReadZipperCore<'_, '_, V> { fn to_next_val(&mut self) -> bool { + timed_span!(ToNextVal); self.to_next_get_value().is_some() } fn descend_first_k_path(&mut self, k: usize) -> bool { + timed_span!(DescendFirstKPath); self.prepare_buffers(); debug_assert!(self.is_regularized()); @@ -1749,6 +1779,7 @@ pub(crate) mod read_zipper_core { self.k_path_internal(k, self.prefix_buf.len()) } fn to_next_k_path(&mut self, k: usize) -> bool { + timed_span!(ToNextKPath); let base_idx = if self.path_len() > k { self.prefix_buf.len() - k } else { @@ -1763,6 +1794,7 @@ pub(crate) mod read_zipper_core { impl<'a, V: Clone + Send + Sync + Unpin> ZipperReadOnlyIteration<'a, V> for ReadZipperCore<'a, '_, V> { fn to_next_get_value(&mut self) -> Option<&'a V> { + timed_span!(ToNextGetValue); self.prepare_buffers(); loop { if self.focus_iter_token == NODE_ITER_INVALID { From f02ab28e13c033e3d5a37bb716807a85b96fe3c3 Mon Sep 17 00:00:00 2001 From: Igor Malovitsa Date: Tue, 3 Jun 2025 16:47:40 -0500 Subject: [PATCH 2/3] allow using more time sources --- src/timed_span.rs | 203 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 162 insertions(+), 41 deletions(-) diff --git a/src/timed_span.rs b/src/timed_span.rs index 005a7e5d..9f206e52 100644 --- a/src/timed_span.rs +++ b/src/timed_span.rs @@ -1,4 +1,3 @@ -use std::arch::asm; use std::sync::atomic::{AtomicU64, Ordering}; macro_rules! entries { @@ -90,57 +89,179 @@ pub fn print_counters() { } } -#[cfg(target_arch = "aarch64")] -#[inline] -fn read_cycle_counter() -> u64 { - let val: u64; - unsafe { - asm!( - "mrs {}, CNTVCT_EL0", - out(reg) val, - options(nostack, nomem) - ); - } - val -} +#[allow(dead_code)] +mod tsc { + use std::arch::asm; + use std::sync::atomic::{AtomicU64, Ordering}; + #[cfg(target_arch = "aarch64")] + #[inline] + fn read_cycle_counter() -> u64 { + let val: u64; + unsafe { + asm!( + "mrs {}, CNTVCT_EL0", + out(reg) val, + options(nostack, nomem) + ); + } + val + } -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -#[inline] -fn read_cycle_counter() -> u64 { - let lo: u32; - let hi: u32; - unsafe { - asm!( - "rdtsc", - out("eax") lo, - out("edx") hi, - options(nostack, nomem) - ); - } - ((hi as u64) << 32) | (lo as u64) -} + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[inline] + fn read_cycle_counter() -> u64 { + let lo: u32; + let hi: u32; + unsafe { + asm!( + "rdtsc", + out("eax") lo, + out("edx") hi, + options(nostack, nomem) + ); + } + ((hi as u64) << 32) | (lo as u64) + } + + pub struct TimedSpanGuard<'a> { + start: u64, + counter: &'a AtomicU64, + } + + impl<'a> TimedSpanGuard<'a> { + pub fn new(counter: &'a AtomicU64) -> Self { + Self { + start: read_cycle_counter(), + counter, + } + } + } -pub struct TimedSpanGuard<'a> { - start: u64, - counter: &'a AtomicU64, + impl<'a> Drop for TimedSpanGuard<'a> { + fn drop(&mut self) { + let end = read_cycle_counter(); + self.counter.fetch_add(end - self.start, Ordering::Relaxed); + } + } } -impl<'a> TimedSpanGuard<'a> { - pub fn new(counter: &'a AtomicU64) -> Self { - Self { - start: read_cycle_counter(), - counter, +#[allow(dead_code)] +mod std_instant { + use std::sync::atomic::{AtomicU64, Ordering}; + use std::time::Instant; + pub struct TimedSpanGuard<'a> { + start: Instant, + counter: &'a AtomicU64, + } + + impl<'a> TimedSpanGuard<'a> { + pub fn new(counter: &'a AtomicU64) -> Self { + Self { + start: Instant::now(), + counter, + } + } + } + + impl<'a> Drop for TimedSpanGuard<'a> { + fn drop(&mut self) { + let duration = self.start.elapsed().as_nanos() as u64; + self.counter.fetch_add(duration, Ordering::Relaxed); } } } -impl<'a> Drop for TimedSpanGuard<'a> { - fn drop(&mut self) { - let end = read_cycle_counter(); - self.counter.fetch_add(end - self.start, Ordering::Relaxed); +#[allow(dead_code)] +mod clock_monotonic { + use std::sync::atomic::{AtomicU64, Ordering}; + + #[cfg(target_os="linux")] + mod linux { + // linux/time.h + // const CLOCK_REALTIME : i32 = 0; + // const CLOCK_MONOTONIC : i32 = 1; + // const CLOCK_PROCESS_CPUTIME_ID: i32 = 2; + // const CLOCK_THREAD_CPUTIME_ID : i32 = 3; + const CLOCK_MONOTONIC_RAW : i32 = 4; + // const CLOCK_REALTIME_COARSE : i32 = 5; + const CLOCK_MONOTONIC_COARSE : i32 = 6; + // const CLOCK_BOOTTIME : i32 = 7; + // const CLOCK_REALTIME_ALARM : i32 = 8; + // const CLOCK_BOOTTIME_ALARM : i32 = 9; + + #[allow(non_camel_case_types)] + #[repr(C)] + struct timespec { + tv_sec: u64, + tv_nsec: i32, + } + + extern "C" { + fn clock_gettime(clockid: i32, tp: &mut timespec) -> i32; + } + pub fn get_time() -> u64 { + let mut time = timespec { tv_sec: 0, tv_nsec: 0 }; + let rv = unsafe { clock_gettime(CLOCK_MONOTONIC_RAW, &mut time) }; + debug_assert!(rv == 0, "failed clock_gettime?"); + time.tv_sec * 1_000_000_000 + time.tv_nsec as u64 + } + } + #[cfg(target_os="linux")] + use linux::get_time; + + #[cfg(all(target_os="macos", target_arch="aarch64"))] + mod macos { + const CLOCK_MONOTONIC_RAW : i32 = 4; + const CLOCK_MONOTONIC_RAW_APPROX: i32 = 5; + const CLOCK_MONOTONIC : i32 = 6; + // const CALENDAR_CLOCK: u64 = 1; + #[allow(non_camel_case_types)] + #[repr(C)] + struct timespec { + tv_sec: u64, + tv_nsec: i32, + } + extern "C" { + // host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + // fn clock_get_time(clock_serv: &mut u64, cur_time: &mut mach_timespec) -> i64; + // mach_port_deallocate(mach_task_self(), cclock); + fn clock_gettime(clock_serv: i32, cur_time: &mut timespec) -> i64; + } + + pub fn get_time() -> u64 { + let mut time = timespec { tv_sec: 0, tv_nsec: 0 }; + let rv = unsafe { clock_gettime(CLOCK_MONOTONIC_RAW, &mut time) }; + debug_assert!(rv == 0, "failed clock_gettime?"); + (time.tv_sec as u64 * 1_000_000_000) + time.tv_nsec as u64 + } + } + + #[cfg(all(target_os="macos", target_arch="aarch64"))] + use macos::get_time; + + pub struct TimedSpanGuard<'a> { + start: u64, + counter: &'a AtomicU64, + } + + impl<'a> TimedSpanGuard<'a> { + pub fn new(counter: &'a AtomicU64) -> Self { + Self { + start: get_time(), + counter, + } + } + } + + impl<'a> Drop for TimedSpanGuard<'a> { + fn drop(&mut self) { + let end = get_time(); + self.counter.fetch_add(end - self.start, Ordering::Relaxed); + } } } +pub use tsc::TimedSpanGuard; pub const ENABLED: bool = true; macro_rules! timed_span { From 4c7cd51113bc571787097564b7c38aa81dd40150 Mon Sep 17 00:00:00 2001 From: Luke Peterson Date: Fri, 21 Aug 2026 23:36:37 -0600 Subject: [PATCH 3/3] Separating the timed_span macro impl into separate span-timing crate, and using that crate Getting rid of timings on Zipper trait default impls, because the inconsistent implementation muddied the results --- Cargo.toml | 1 + src/arena_compact.rs | 43 +++---- src/timed_span.rs | 281 +++++-------------------------------------- src/zipper.rs | 52 +++----- 4 files changed, 73 insertions(+), 304 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ad532ec4..6bdb0345 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ name = "pathmap" path = "src/lib.rs" [dependencies] +span-timing = { version = "0.1.0", path = "../span-timing" } pathmap-derive = { version = "0.2.1", path = "./pathmap-derive" } maybe-dangling = "0.1.1" dyn-clone = "1.0.17" # NOTE, we can eliminate this when we eliminate `!slim_ptrs` diff --git a/src/arena_compact.rs b/src/arena_compact.rs index 504f0164..ed8a65d0 100644 --- a/src/arena_compact.rs +++ b/src/arena_compact.rs @@ -82,10 +82,10 @@ use std::marker::PhantomData; use fast_slice_utils::starts_with; use crate::alloc::{GlobalAlloc, global_alloc}; +use crate::timed_span::{TimingEntries::*, COUNTERS, timed_span}; use crate::{ PathMap, morphisms::Catamorphism, - timed_span::timed_span, utils::{BitMask, ByteMask, find_prefix_overlap}, zipper::{ Zipper, ZipperValues, ZipperForking, ZipperAbsolutePath, ZipperIteration, @@ -93,7 +93,6 @@ use crate::{ ZipperConcrete, ZipperReadOnlyConditionalValues, TrieRef }, }; - use crate::gxhash::{GxHasher, HashMap, HashMapExt}; /// The identifier of a node (branch node or line node) @@ -2887,7 +2886,7 @@ where Storage: AsRef<[u8]> /// Resets the zipper's focus back to the root fn reset(&mut self) { - timed_span!(Reset); + timed_span!(Reset, COUNTERS); // self.ascend(self.path.len() - self.origin_depth); let (cur_node, _) = self.tree.get_node(self.stack[0].node_id); self.cur_node = cur_node; @@ -2904,7 +2903,7 @@ where Storage: AsRef<[u8]> /// /// WARNING: This is not a cheap method. It may have an order-N cost fn val_count(&self) -> usize { - timed_span!(ValueCount); + timed_span!(ValueCount, COUNTERS); let mut zipper = self.clone(); zipper.reset(); let mut count = 0; @@ -2922,7 +2921,7 @@ where Storage: AsRef<[u8]> /// Returns `true` if the zipper points to an existing path within the tree, otherwise `false`. The /// zipper's location will be updated, regardless of whether or not the path exists within the tree. fn descend_to>(&mut self, path: P) { - timed_span!(DescendTo); + timed_span!(DescendTo, COUNTERS); let path = path.as_ref(); let depth = path.len(); let descended = self.descend_to_existing(path); @@ -2939,7 +2938,7 @@ where Storage: AsRef<[u8]> /// existing path after this method returns, unless the method was called with the focus on a /// non-existent path. fn descend_to_existing>(&mut self, path: P) -> usize { - timed_span!(DescendToExisting); + timed_span!(DescendToExisting, COUNTERS); self.descend_cond(path.as_ref(), false) } @@ -2950,19 +2949,15 @@ where Storage: AsRef<[u8]> /// /// If the focus is already on a value, this method will descend to the *next* value along /// the path. - fn descend_to_value>(&mut self, path: K) -> usize { - timed_span!(DescendToValue); - self.descend_cond(path.as_ref(), true) - } - fn descend_to_val>(&mut self, path: K) -> usize { + timed_span!(DescendToVal, COUNTERS); self.descend_cond(path.as_ref(), true) } /// Moves the zipper one byte deeper into the trie. Identical in effect to [descend_to](Self::descend_to) /// with a 1-byte key argument fn descend_to_byte(&mut self, k: u8) { - timed_span!(DescendToByte); + timed_span!(DescendToByte, COUNTERS); self.descend_to(&[k]) } @@ -2974,7 +2969,7 @@ where Storage: AsRef<[u8]> /// to the trie. This method should only be used as part of a directed traversal operation, but /// index-based paths may not be stored as locations within the trie. fn descend_indexed_byte(&mut self, idx: usize) -> bool { - timed_span!(DescendIndexedBranch); + timed_span!(DescendIndexedByte, COUNTERS); if self.invalid > 0 { return false; } @@ -3029,14 +3024,14 @@ where Storage: AsRef<[u8]> /// NOTE: This method should have identical behavior to passing `0` to [descend_indexed_byte](ZipperMoving::descend_indexed_byte), /// although with less overhead fn descend_first_byte(&mut self) -> bool { - timed_span!(DescendFirstByte); + timed_span!(DescendFirstByte, COUNTERS); self.descend_indexed_byte(0) } /// Descends the zipper's focus until a branch or a value is encountered. Returns `true` if the focus /// moved otherwise returns `false` fn descend_until(&mut self) -> bool { - timed_span!(DescendUntil); + timed_span!(DescendUntil, COUNTERS); self.trace_pos(); let mut descended = false; 'descend: while self.child_count() == 1 { @@ -3087,7 +3082,7 @@ where Storage: AsRef<[u8]> /// If the root is fewer than `n` steps from the zipper's position, then this method will stop at /// the root and return `false` fn ascend(&mut self, mut steps: usize) -> bool { - timed_span!(Ascend); + timed_span!(Ascend, COUNTERS); self.trace_pos(); if !self.ascend_invalid(Some(&mut steps)) { return false; @@ -3114,14 +3109,14 @@ where Storage: AsRef<[u8]> /// Ascends the zipper up a single byte. Equivalent to passing `1` to [ascend](Self::ascend) fn ascend_byte(&mut self) -> bool { - timed_span!(AscendByte); + timed_span!(AscendByte, COUNTERS); self.ascend(1) } /// Ascends the zipper to the nearest upstream branch point or value. Returns `true` if the zipper /// focus moved upwards, otherwise returns `false` if the zipper was already at the root fn ascend_until(&mut self) -> bool { - timed_span!(AscendUntil); + timed_span!(AscendUntil, COUNTERS); self.ascend_to_branch(true) } @@ -3129,19 +3124,19 @@ where Storage: AsRef<[u8]> /// `true` if the zipper focus moved upwards, otherwise returns `false` if the zipper was already at the /// root fn ascend_until_branch(&mut self) -> bool { - timed_span!(AscendUntilBranch); + timed_span!(AscendUntilBranch, COUNTERS); self.ascend_to_branch(false) } #[inline] fn to_next_sibling_byte(&mut self) -> bool { - timed_span!(ToNextSiblingByte); + timed_span!(ToNextSiblingByte, COUNTERS); self.to_sibling(true) } #[inline] fn to_prev_sibling_byte(&mut self) -> bool { - timed_span!(ToPrevSiblingByte); + timed_span!(ToPrevSiblingByte, COUNTERS); self.to_sibling(false) } @@ -3157,7 +3152,7 @@ where Storage: AsRef<[u8]> /// /// Returns a reference to the value or `None` if the zipper has encountered the root. fn to_next_val(&mut self) -> bool { - timed_span!(ToNextVal); + timed_span!(ToNextVal, COUNTERS); while self.to_next_step() { if self.is_val() { return true; @@ -3177,7 +3172,7 @@ where Storage: AsRef<[u8]> /// /// See: [to_next_k_path](ZipperIteration::to_next_k_path) fn descend_first_k_path(&mut self, k: usize) -> bool { - timed_span!(DescendFirstKPath); + timed_span!(DescendFirstKPath, COUNTERS); for ii in 0..k { if !self.descend_first_byte() { self.ascend(ii); @@ -3199,7 +3194,7 @@ where Storage: AsRef<[u8]> /// /// See: [descend_first_k_path](ZipperIteration::descend_first_k_path) fn to_next_k_path(&mut self, k: usize) -> bool { - timed_span!(ToNextKPath); + timed_span!(ToNextKPath, COUNTERS); let mut depth = k; 'outer: loop { while depth > 0 && self.child_count() <= 1 { diff --git a/src/timed_span.rs b/src/timed_span.rs index 718b75dd..5a73dce1 100644 --- a/src/timed_span.rs +++ b/src/timed_span.rs @@ -1,48 +1,41 @@ -use std::sync::atomic::{AtomicU64, Ordering}; +use core::sync::atomic::Ordering; -macro_rules! entries { - ( - pub enum $name:ident { - $($entry:ident $(= $value:expr)?),* - $(,)? - } - ) => { - #[derive(Clone, Copy)] - pub enum $name { - $($entry $(= $value)?),* - } - impl $name { - const ALL: &[$name] = &[$($name::$entry),*]; - const COUNT: usize = $name::ALL.len(); - pub fn to_str(&self) -> &'static str { - match self { - $( - $name::$entry => stringify!($entry) - ),* - } - } - } +use span_timing::{Counter, timing_entries}; - } +/// Set to `true` to enable timing on ReadZipperCore and ACTZipper ops. Currently not implemented for +/// other zipper types, except insofar as they call through to a zipper with the implementation. +pub(crate) const ENABLED: bool = false; + +/// Wrapper macro to enable and disable span timing +macro_rules! timed_span { + ($entry:expr, $counters:expr $(,)?) => { + let _timed_span_guard = if $crate::timed_span::ENABLED { + Some(::span_timing::timed_span!($entry, $counters)) + } else { + None + }; + }; } -entries!{ - pub enum Entries { +pub(crate) use timed_span; + +timing_entries! { + pub enum TimingEntries { Reset, ValueCount, DescendTo, DescendToExisting, - DescendToValue, + DescendToVal, //ReadZipperCore doesn't have a native impl for this method yet DescendToByte, - DescendIndexedBranch, + DescendIndexedByte, DescendFirstByte, DescendUntil, - MoveToPath, + // MoveToPath, This is only implemented with a default impl, and it's composed from other zipper ops AscendByte, Ascend, ToNextSiblingByte, ToPrevSiblingByte, - ToNextStep, + // ToNextStep, This is only implemented with a default impl, and it's composed from other zipper ops AscendUntil, AscendUntilBranch, ToNextVal, @@ -51,237 +44,29 @@ entries!{ ToNextGetValue, ForkReadZipper, } + pub static COUNTERS: [Counter]; } -pub struct Counter { - pub count: AtomicU64, - pub cycles: AtomicU64, -} - -impl Counter { - const fn new() -> Self { - Self { - count: AtomicU64::new(0), - cycles: AtomicU64::new(0), - } - } -} - -pub static COUNTERS: [Counter; Entries::COUNT] = - [const { Counter::new() }; Entries::COUNT]; - pub fn reset_counters() { for counter in &COUNTERS { - counter.count.store(0, Ordering::Relaxed); - counter.cycles.store(0, Ordering::Relaxed); + counter.reset(); } } pub fn print_counters() { - println!("{:>20},Count,TSCDelta,TSCAverage", "Name"); - for &entry in Entries::ALL { + println!("{:>20},Count,TicksDelta,TicksAverage", "Name"); + for &entry in TimingEntries::ALL { let counter = &COUNTERS[entry as usize]; let count = counter.count.load(Ordering::Relaxed); - let cycles = counter.cycles.load(Ordering::Relaxed); - if count == 0 && cycles == 0 { continue; } - let average = cycles as f64 / count as f64; - println!("{:>20},{},{},{}", entry.to_str(), count, cycles, average); - } -} - -#[allow(dead_code)] -mod tsc { - use std::arch::asm; - use std::sync::atomic::{AtomicU64, Ordering}; - #[cfg(target_arch = "aarch64")] - #[inline] - fn read_cycle_counter() -> u64 { - let val: u64; - unsafe { - asm!( - "mrs {}, CNTVCT_EL0", - out(reg) val, - options(nostack, nomem) - ); - } - val - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[inline] - fn read_cycle_counter() -> u64 { - let lo: u32; - let hi: u32; - unsafe { - asm!( - "rdtsc", - out("eax") lo, - out("edx") hi, - options(nostack, nomem) - ); - } - ((hi as u64) << 32) | (lo as u64) - } - - pub struct TimedSpanGuard<'a> { - start: u64, - counter: &'a AtomicU64, - } - - impl<'a> TimedSpanGuard<'a> { - pub fn new(counter: &'a AtomicU64) -> Self { - Self { - start: read_cycle_counter(), - counter, - } - } - } - - impl<'a> Drop for TimedSpanGuard<'a> { - fn drop(&mut self) { - let end = read_cycle_counter(); - self.counter.fetch_add(end - self.start, Ordering::Relaxed); + let ticks = counter.ticks.load(Ordering::Relaxed); + if count == 0 && ticks == 0 { + continue; } + let average = ticks as f64 / count as f64; + println!("{:>20},{},{},{}", entry.to_str(), count, ticks, average); } } -#[allow(dead_code)] -mod std_instant { - use std::sync::atomic::{AtomicU64, Ordering}; - use std::time::Instant; - pub struct TimedSpanGuard<'a> { - start: Instant, - counter: &'a AtomicU64, - } - - impl<'a> TimedSpanGuard<'a> { - pub fn new(counter: &'a AtomicU64) -> Self { - Self { - start: Instant::now(), - counter, - } - } - } - - impl<'a> Drop for TimedSpanGuard<'a> { - fn drop(&mut self) { - let duration = self.start.elapsed().as_nanos() as u64; - self.counter.fetch_add(duration, Ordering::Relaxed); - } - } -} - -#[allow(dead_code)] -mod clock_monotonic { - use std::sync::atomic::{AtomicU64, Ordering}; - - #[cfg(target_os="linux")] - mod linux { - // linux/time.h - // const CLOCK_REALTIME : i32 = 0; - // const CLOCK_MONOTONIC : i32 = 1; - // const CLOCK_PROCESS_CPUTIME_ID: i32 = 2; - // const CLOCK_THREAD_CPUTIME_ID : i32 = 3; - const CLOCK_MONOTONIC_RAW : i32 = 4; - // const CLOCK_REALTIME_COARSE : i32 = 5; - const CLOCK_MONOTONIC_COARSE : i32 = 6; - // const CLOCK_BOOTTIME : i32 = 7; - // const CLOCK_REALTIME_ALARM : i32 = 8; - // const CLOCK_BOOTTIME_ALARM : i32 = 9; - - #[allow(non_camel_case_types)] - #[repr(C)] - struct timespec { - tv_sec: u64, - tv_nsec: i32, - } - - unsafe extern "C" { - fn clock_gettime(clockid: i32, tp: &mut timespec) -> i32; - } - pub fn get_time() -> u64 { - let mut time = timespec { tv_sec: 0, tv_nsec: 0 }; - let rv = unsafe { clock_gettime(CLOCK_MONOTONIC_RAW, &mut time) }; - debug_assert!(rv == 0, "failed clock_gettime?"); - time.tv_sec * 1_000_000_000 + time.tv_nsec as u64 - } - } - #[cfg(target_os="linux")] - use linux::get_time; - - #[cfg(all(target_os="macos", target_arch="aarch64"))] - mod macos { - const CLOCK_MONOTONIC_RAW : i32 = 4; - const CLOCK_MONOTONIC_RAW_APPROX: i32 = 5; - const CLOCK_MONOTONIC : i32 = 6; - // const CALENDAR_CLOCK: u64 = 1; - #[allow(non_camel_case_types)] - #[repr(C)] - struct timespec { - tv_sec: u64, - tv_nsec: i32, - } - unsafe extern "C" { - // host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); - // fn clock_get_time(clock_serv: &mut u64, cur_time: &mut mach_timespec) -> i64; - // mach_port_deallocate(mach_task_self(), cclock); - fn clock_gettime(clock_serv: i32, cur_time: &mut timespec) -> i64; - } - - pub fn get_time() -> u64 { - let mut time = timespec { tv_sec: 0, tv_nsec: 0 }; - let rv = unsafe { clock_gettime(CLOCK_MONOTONIC_RAW, &mut time) }; - debug_assert!(rv == 0, "failed clock_gettime?"); - (time.tv_sec as u64 * 1_000_000_000) + time.tv_nsec as u64 - } - } - - #[cfg(all(target_os="macos", target_arch="aarch64"))] - use macos::get_time; - - pub struct TimedSpanGuard<'a> { - start: u64, - counter: &'a AtomicU64, - } - - impl<'a> TimedSpanGuard<'a> { - pub fn new(counter: &'a AtomicU64) -> Self { - Self { - start: get_time(), - counter, - } - } - } - - impl<'a> Drop for TimedSpanGuard<'a> { - fn drop(&mut self) { - let end = get_time(); - self.counter.fetch_add(end - self.start, Ordering::Relaxed); - } - } -} - -pub use tsc::TimedSpanGuard; -pub const ENABLED: bool = true; - -macro_rules! timed_span { - ($name:ident, $dst:path) => { - let _guard = if $crate::timed_span::ENABLED { - let index = $crate::timed_span::Entries::$name as usize; - let counter = &$dst[index]; - counter.count.fetch_add(1, core::sync::atomic::Ordering::Relaxed); - Some($crate::timed_span::TimedSpanGuard::new(&counter.cycles)) - } else { - None - }; - }; - ($name:ident) => { - $crate::timed_span::timed_span!($name, $crate::timed_span::COUNTERS) - }; -} - -pub(crate) use timed_span; - #[cfg(test)] mod tests { use super::*; @@ -290,7 +75,7 @@ mod tests { fn test_timed_span() { reset_counters(); { - timed_span!(Reset); + timed_span!(TimingEntries::Reset, COUNTERS); for ii in 0..100_000 { core::hint::black_box(ii); } diff --git a/src/zipper.rs b/src/zipper.rs index a59d0a75..ada57c47 100644 --- a/src/zipper.rs +++ b/src/zipper.rs @@ -13,7 +13,7 @@ use crate::alloc::{Allocator, GlobalAlloc}; use crate::utils::ByteMask; use crate::trie_node::*; use crate::PathMap; -use crate::timed_span::timed_span; +use crate::timed_span::{TimingEntries::*, COUNTERS, timed_span}; pub use crate::write_zipper::*; pub use crate::trie_ref::*; @@ -140,7 +140,6 @@ pub trait ZipperMoving: Zipper { /// Resets the zipper's focus back to its root fn reset(&mut self) { - timed_span!(Reset); while !self.at_root() { self.ascend_byte(); } @@ -159,7 +158,6 @@ pub trait ZipperMoving: Zipper { /// /// Returns the number of bytes shared between the old and new location fn move_to_path>(&mut self, path: K) -> usize { - timed_span!(MoveToPath); let path = path.as_ref(); let p = self.path(); let overlap = find_prefix_overlap(path, p); @@ -192,7 +190,6 @@ pub trait ZipperMoving: Zipper { /// existing path after this method returns, unless the method was called with the focus on a /// non-existent path. fn descend_to_existing>(&mut self, k: K) -> usize { - timed_span!(DescendToExisting); let k = k.as_ref(); let mut i = 0; while i < k.len() { @@ -215,7 +212,6 @@ pub trait ZipperMoving: Zipper { /// the path. //GOAT. this default implementation could certainly be optimized fn descend_to_val>(&mut self, k: K) -> usize { - timed_span!(DescendToValue); let k = k.as_ref(); let mut i = 0; while i < k.len() { @@ -241,7 +237,6 @@ pub trait ZipperMoving: Zipper { /// Moves the zipper one byte deeper into the trie. Identical in effect to [descend_to](Self::descend_to) /// with a 1-byte key argument fn descend_to_byte(&mut self, k: u8) { - timed_span!(DescendToByte); self.descend_to(&[k]) } @@ -266,7 +261,6 @@ pub trait ZipperMoving: Zipper { /// to the trie. This method should only be used as part of a directed traversal operation, but /// index-based paths may not be stored as locations within the trie. fn descend_indexed_byte(&mut self, idx: usize) -> bool { - timed_span!(DescendIndexedBranch); let mask = self.child_mask(); let child_byte = match mask.indexed_bit::(idx) { Some(byte) => byte, @@ -290,7 +284,6 @@ pub trait ZipperMoving: Zipper { /// NOTE: This method should have identical behavior to passing `0` to [descend_indexed_byte](ZipperMoving::descend_indexed_byte), /// although with less overhead fn descend_first_byte(&mut self) -> bool { - timed_span!(DescendFirstByte); self.descend_indexed_byte(0) } @@ -312,7 +305,6 @@ pub trait ZipperMoving: Zipper { /// /// Does nothing and returns `false` if the zipper's focus is on a non-existent path. fn descend_until(&mut self) -> bool { - timed_span!(DescendUntil); let mut descended = false; while self.child_count() == 1 { descended = true; @@ -353,7 +345,6 @@ pub trait ZipperMoving: Zipper { /// Ascends the zipper up a single byte. Equivalent to passing `1` to [ascend](Self::ascend) fn ascend_byte(&mut self) -> bool { - timed_span!(AscendByte); self.ascend(1) } @@ -378,7 +369,6 @@ pub trait ZipperMoving: Zipper { /// This method is equivalent to calling [ZipperMoving::ascend] with `1`, followed by [ZipperMoving::descend_indexed_byte] /// where the index passed is 1 more than the index of the current focus position. fn to_next_sibling_byte(&mut self) -> bool { - timed_span!(ToNextSiblingByte); let cur_byte = match self.path().last() { Some(byte) => *byte, None => return false @@ -408,7 +398,6 @@ pub trait ZipperMoving: Zipper { /// This method is equivalent to calling [Self::ascend] with `1`, followed by [Self::descend_indexed_byte] /// where the index passed is 1 less than the index of the current focus position. fn to_prev_sibling_byte(&mut self) -> bool { - timed_span!(ToPrevSiblingByte); let cur_byte = match self.path().last() { Some(byte) => *byte, None => return false @@ -436,7 +425,6 @@ pub trait ZipperMoving: Zipper { /// Returns `true` if the position of the zipper has moved, or `false` if the zipper has returned /// to the root fn to_next_step(&mut self) -> bool { - timed_span!(ToNextStep); //If we're at a leaf ascend until we're not and jump to the next sibling if self.child_count() == 0 { @@ -1489,7 +1477,7 @@ pub(crate) mod read_zipper_core { impl ZipperForking for ReadZipperCore<'_, '_, V, A> { type ReadZipperT<'a> = ReadZipperCore<'a, 'a, V, A> where Self: 'a; fn fork_read_zipper<'a>(&'a self) -> Self::ReadZipperT<'a> { - timed_span!(ForkReadZipper); + timed_span!(ForkReadZipper, COUNTERS); let new_root_val = self.val(); let new_root_path = self.origin_path(); let new_root_key_start = new_root_path.len() - self.node_key().len(); @@ -1572,7 +1560,7 @@ pub(crate) mod read_zipper_core { } fn reset(&mut self) { - timed_span!(Reset); + timed_span!(Reset, COUNTERS); self.ancestors.truncate(1); match self.ancestors.pop() { Some((node, _tok, _prefix_len)) => { @@ -1594,7 +1582,7 @@ pub(crate) mod read_zipper_core { } fn val_count(&self) -> usize { - timed_span!(ValueCount); + timed_span!(ValueCount, COUNTERS); let root_val = self.is_val() as usize; if self.node_key().len() == 0 { val_count_below_root(*self.focus_node) + root_val @@ -1608,7 +1596,7 @@ pub(crate) mod read_zipper_core { } } fn descend_to>(&mut self, k: K) { - timed_span!(DescendTo); + timed_span!(DescendTo, COUNTERS); let k = k.as_ref(); if k.len() == 0 { return //Zero-length path is a no-op @@ -1640,7 +1628,7 @@ pub(crate) mod read_zipper_core { #[inline] fn descend_to_byte(&mut self, k: u8) { - timed_span!(DescendToByte); + timed_span!(DescendToByte, COUNTERS); self.prepare_buffers(); debug_assert!(self.is_regularized()); @@ -1676,7 +1664,7 @@ pub(crate) mod read_zipper_core { } fn descend_indexed_byte(&mut self, child_idx: usize) -> bool { - timed_span!(DescendIndexedBranch); + timed_span!(DescendIndexedByte, COUNTERS); self.prepare_buffers(); debug_assert!(self.is_regularized()); @@ -1698,7 +1686,7 @@ pub(crate) mod read_zipper_core { } fn descend_first_byte(&mut self) -> bool { - timed_span!(DescendFirstByte); + timed_span!(DescendFirstByte, COUNTERS); self.prepare_buffers(); debug_assert!(self.is_regularized()); let cur_tok = self.focus_node.iter_token_for_path(self.node_key()); @@ -1740,7 +1728,7 @@ pub(crate) mod read_zipper_core { } fn descend_until(&mut self) -> bool { - timed_span!(DescendUntil); + timed_span!(DescendUntil, COUNTERS); debug_assert!(self.is_regularized()); let mut moved = false; while self.child_count() == 1 { @@ -1801,7 +1789,7 @@ pub(crate) mod read_zipper_core { } fn descend_to_existing>(&mut self, k: K) -> usize { - timed_span!(DescendToExisting); + timed_span!(DescendToExisting, COUNTERS); let mut k = k.as_ref(); if k.len() == 0 { return 0 //Zero-length path is a no-op @@ -1867,7 +1855,7 @@ pub(crate) mod read_zipper_core { // } fn to_next_sibling_byte(&mut self) -> bool { - timed_span!(ToNextSiblingByte); + timed_span!(ToNextSiblingByte, COUNTERS); self.prepare_buffers(); if self.prefix_buf.len() == 0 { return false @@ -1928,12 +1916,12 @@ pub(crate) mod read_zipper_core { } fn to_prev_sibling_byte(&mut self) -> bool { - timed_span!(ToPrevSiblingByte); + timed_span!(ToPrevSiblingByte, COUNTERS); self.to_sibling(false) } fn ascend(&mut self, mut steps: usize) -> bool { - timed_span!(Ascend); + timed_span!(Ascend, COUNTERS); debug_assert!(self.is_regularized()); while steps > 0 { if self.excess_key_len() == 0 { @@ -1957,7 +1945,7 @@ pub(crate) mod read_zipper_core { } fn ascend_byte(&mut self) -> bool { - timed_span!(AscendByte); + timed_span!(AscendByte, COUNTERS); debug_assert!(self.is_regularized()); if self.excess_key_len() == 0 { match self.ancestors.pop() { @@ -1977,7 +1965,7 @@ pub(crate) mod read_zipper_core { } fn ascend_until(&mut self) -> bool { - timed_span!(AscendUntil); + timed_span!(AscendUntil, COUNTERS); debug_assert!(self.is_regularized()); if self.at_root() { return false; @@ -1994,7 +1982,7 @@ pub(crate) mod read_zipper_core { } fn ascend_until_branch(&mut self) -> bool { - timed_span!(AscendUntilBranch); + timed_span!(AscendUntilBranch, COUNTERS); debug_assert!(self.is_regularized()); if self.at_root() { return false; @@ -2137,11 +2125,11 @@ pub(crate) mod read_zipper_core { impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> ZipperIteration for ReadZipperCore<'trie, '_, V, A> { fn to_next_val(&mut self) -> bool { - timed_span!(ToNextVal); + timed_span!(ToNextVal, COUNTERS); unsafe{ self.to_next_get_val() }.is_some() } fn descend_first_k_path(&mut self, k: usize) -> bool { - timed_span!(DescendFirstKPath); + timed_span!(DescendFirstKPath, COUNTERS); self.prepare_buffers(); debug_assert!(self.is_regularized()); @@ -2151,7 +2139,7 @@ pub(crate) mod read_zipper_core { self.k_path_internal(k, self.prefix_buf.len()) } fn to_next_k_path(&mut self, k: usize) -> bool { - timed_span!(ToNextKPath); + timed_span!(ToNextKPath, COUNTERS); let base_idx = if self.path_len() >= k { self.prefix_buf.len() - k } else { @@ -2390,7 +2378,7 @@ pub(crate) mod read_zipper_core { /// See [ReadZipperCore::get_val] for explanation as to why this is unsafe pub(crate) unsafe fn to_next_get_val(&mut self) -> Option<&'a V> { - timed_span!(ToNextGetValue); + timed_span!(ToNextGetValue, COUNTERS); self.prepare_buffers(); loop { if self.focus_iter_token == NODE_ITER_INVALID {