Skip to content

Commit d7dca40

Browse files
committed
added counters to zippers
1 parent eb9caff commit d7dca40

File tree

5 files changed

+240
-1
lines changed

5 files changed

+240
-1
lines changed

examples/arena_compact_tests/src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ fn arena_create() -> Result<(), std::io::Error> {
2929
println!("len {:.2?}", tree.get_data().len());
3030
// pathmap::alloc_tracking::read().print();
3131
// pathmap::alloc_tracking::reset();
32+
pathmap::timed_span::print_counters();
33+
pathmap::timed_span::reset_counters();
3234

3335
let start = Instant::now();
3436
let mut zipper = tree.read_zipper();
@@ -38,6 +40,8 @@ fn arena_create() -> Result<(), std::io::Error> {
3840
assert!(zipper.descend_to_existing(path) == path.len());
3941
// assert_eq!(zipper.path(), path);
4042
}
43+
pathmap::timed_span::print_counters();
44+
4145
println!("checked act in {:.2?}", start.elapsed());
4246
let start = Instant::now();
4347
let tree2 = ArenaCompactTree::from_zipper(tree.read_zipper(), |_v| 0);
@@ -78,6 +82,7 @@ fn arena_dump() -> Result<(), std::io::Error> {
7882
assert!(zipper.descend_to_existing(path) == path.len());
7983
// assert_eq!(zipper.path(), path);
8084
}
85+
pathmap::timed_span::print_counters();
8186
println!("checked act in {:.2?}", start.elapsed());
8287
let start = Instant::now();
8388
let tree2 = ArenaCompactTree::from_zipper(tree.read_zipper(), |_v| 0);

src/arena_compact.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
use crate::{
8080
morphisms::Catamorphism,
8181
utils::{BitMask, ByteMask, find_prefix_overlap},
82+
timed_span::timed_span,
8283
zipper::{
8384
Zipper, ZipperValues, ZipperForking, ZipperAbsolutePath, ZipperIteration,
8485
ZipperMoving, ZipperPathBuffer, ZipperReadOnlyValues,
@@ -1657,10 +1658,13 @@ impl<'tree, Storage> ZipperMoving for ACTZipper<'tree, Storage>
16571658
where Storage: AsRef<[u8]>
16581659
{
16591660
/// Returns `true` if the zipper cannot ascend further, otherwise returns `false`
1660-
fn at_root(&self) -> bool { self.path.len() <= self.origin_depth }
1661+
fn at_root(&self) -> bool {
1662+
self.path.len() <= self.origin_depth
1663+
}
16611664

16621665
/// Resets the zipper's focus back to the root
16631666
fn reset(&mut self) {
1667+
timed_span!(Reset);
16641668
// self.ascend(self.path.len() - self.origin_depth);
16651669
self.path.truncate(self.origin_depth);
16661670
self.cur_node = self.tree.get_node(self.stack[0].node_id).0;
@@ -1675,6 +1679,7 @@ where Storage: AsRef<[u8]>
16751679
///
16761680
/// WARNING: This is not a cheap method. It may have an order-N cost
16771681
fn val_count(&self) -> usize {
1682+
timed_span!(ValueCount);
16781683
let mut zipper = self.clone();
16791684
zipper.reset();
16801685
let mut count = 0;
@@ -1692,6 +1697,7 @@ where Storage: AsRef<[u8]>
16921697
/// Returns `true` if the zipper points to an existing path within the tree, otherwise `false`. The
16931698
/// zipper's location will be updated, regardless of whether or not the path exists within the tree.
16941699
fn descend_to<P: AsRef<[u8]>>(&mut self, path: P) -> bool {
1700+
timed_span!(DescendTo);
16951701
let path = path.as_ref();
16961702
let depth = path.len();
16971703
let descended = self.descend_to_existing(path);
@@ -1711,6 +1717,7 @@ where Storage: AsRef<[u8]>
17111717
/// existing path after this method returns, unless the method was called with the focus on a
17121718
/// non-existent path.
17131719
fn descend_to_existing<P: AsRef<[u8]>>(&mut self, path: P) -> usize {
1720+
timed_span!(DescendToExisting);
17141721
self.descend_cond(path.as_ref(), false)
17151722
}
17161723

@@ -1722,12 +1729,14 @@ where Storage: AsRef<[u8]>
17221729
/// If the focus is already on a value, this method will descend to the *next* value along
17231730
/// the path.
17241731
fn descend_to_value<K: AsRef<[u8]>>(&mut self, path: K) -> usize {
1732+
timed_span!(DescendToValue);
17251733
self.descend_cond(path.as_ref(), true)
17261734
}
17271735

17281736
/// Moves the zipper one byte deeper into the trie. Identical in effect to [descend_to](Self::descend_to)
17291737
/// with a 1-byte key argument
17301738
fn descend_to_byte(&mut self, k: u8) -> bool {
1739+
timed_span!(DescendToByte);
17311740
self.descend_to(&[k])
17321741
}
17331742

@@ -1739,6 +1748,7 @@ where Storage: AsRef<[u8]>
17391748
/// to the trie. This method should only be used as part of a directed traversal operation, but
17401749
/// index-based paths may not be stored as locations within the trie.
17411750
fn descend_indexed_branch(&mut self, idx: usize) -> bool {
1751+
timed_span!(DescendIndexedBranch);
17421752
if self.invalid > 0 {
17431753
return false;
17441754
}
@@ -1793,12 +1803,14 @@ where Storage: AsRef<[u8]>
17931803
/// NOTE: This method should have identical behavior to passing `0` to [descend_indexed_branch](ZipperMoving::descend_indexed_branch),
17941804
/// although with less overhead
17951805
fn descend_first_byte(&mut self) -> bool {
1806+
timed_span!(DescendFirstByte);
17961807
self.descend_indexed_branch(0)
17971808
}
17981809

17991810
/// Descends the zipper's focus until a branch or a value is encountered. Returns `true` if the focus
18001811
/// moved otherwise returns `false`
18011812
fn descend_until(&mut self) -> bool {
1813+
timed_span!(DescendUntil);
18021814
self.trace_pos();
18031815
let mut descended = false;
18041816
'descend: while self.child_count() == 1 {
@@ -1849,6 +1861,7 @@ where Storage: AsRef<[u8]>
18491861
/// If the root is fewer than `n` steps from the zipper's position, then this method will stop at
18501862
/// the root and return `false`
18511863
fn ascend(&mut self, mut steps: usize) -> bool {
1864+
timed_span!(Ascend);
18521865
self.trace_pos();
18531866
if !self.ascend_invalid(Some(steps)) {
18541867
return false;
@@ -1875,29 +1888,34 @@ where Storage: AsRef<[u8]>
18751888

18761889
/// Ascends the zipper up a single byte. Equivalent to passing `1` to [ascend](Self::ascend)
18771890
fn ascend_byte(&mut self) -> bool {
1891+
timed_span!(AscendByte);
18781892
self.ascend(1)
18791893
}
18801894

18811895
/// Ascends the zipper to the nearest upstream branch point or value. Returns `true` if the zipper
18821896
/// focus moved upwards, otherwise returns `false` if the zipper was already at the root
18831897
fn ascend_until(&mut self) -> bool {
1898+
timed_span!(AscendUntil);
18841899
self.ascend_to_branch(true)
18851900
}
18861901

18871902
/// Ascends the zipper to the nearest upstream branch point, skipping over values along the way. Returns
18881903
/// `true` if the zipper focus moved upwards, otherwise returns `false` if the zipper was already at the
18891904
/// root
18901905
fn ascend_until_branch(&mut self) -> bool {
1906+
timed_span!(AscendUntilBranch);
18911907
self.ascend_to_branch(false)
18921908
}
18931909

18941910
#[inline]
18951911
fn to_next_sibling_byte(&mut self) -> bool {
1912+
timed_span!(ToNextSiblingByte);
18961913
self.to_sibling(true)
18971914
}
18981915

18991916
#[inline]
19001917
fn to_prev_sibling_byte(&mut self) -> bool {
1918+
timed_span!(ToPrevSiblingByte);
19011919
self.to_sibling(false)
19021920
}
19031921

@@ -1913,6 +1931,7 @@ where Storage: AsRef<[u8]>
19131931
///
19141932
/// Returns a reference to the value or `None` if the zipper has encountered the root.
19151933
fn to_next_val(&mut self) -> bool {
1934+
timed_span!(ToNextVal);
19161935
while self.to_next_step() {
19171936
if self.is_value() {
19181937
return true;
@@ -1932,6 +1951,7 @@ where Storage: AsRef<[u8]>
19321951
///
19331952
/// See: [to_next_k_path](ZipperIteration::to_next_k_path)
19341953
fn descend_first_k_path(&mut self, k: usize) -> bool {
1954+
timed_span!(DescendFirstKPath);
19351955
for ii in 0..k {
19361956
if !self.descend_first_byte() {
19371957
self.ascend(ii);
@@ -1953,6 +1973,7 @@ where Storage: AsRef<[u8]>
19531973
///
19541974
/// See: [descend_first_k_path](ZipperIteration::descend_first_k_path)
19551975
fn to_next_k_path(&mut self, k: usize) -> bool {
1976+
timed_span!(ToNextKPath);
19561977
let mut depth = k;
19571978
'outer: loop {
19581979
while depth > 0 && self.child_count() <= 1 {

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ pub mod fuzzer;
5252
#[cfg(feature = "counters")]
5353
pub mod counters;
5454

55+
pub mod timed_span;
56+
5557
pub mod serialization;
5658
pub mod path_serialization;
5759
pub mod tree_serialization;

src/timed_span.rs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
use std::arch::asm;
2+
use std::sync::atomic::{AtomicU64, Ordering};
3+
4+
macro_rules! entries {
5+
(
6+
pub enum $name:ident {
7+
$($entry:ident $(= $value:expr)?),*
8+
$(,)?
9+
}
10+
) => {
11+
#[derive(Clone, Copy)]
12+
pub enum $name {
13+
$($entry $(= $value)?),*
14+
}
15+
impl $name {
16+
const ALL: &[$name] = &[$($name::$entry),*];
17+
const COUNT: usize = $name::ALL.len();
18+
pub fn to_str(&self) -> &'static str {
19+
match self {
20+
$(
21+
$name::$entry => stringify!($entry)
22+
),*
23+
}
24+
}
25+
}
26+
27+
}
28+
}
29+
30+
entries!{
31+
pub enum Entries {
32+
Reset,
33+
ValueCount,
34+
DescendTo,
35+
DescendToExisting,
36+
DescendToValue,
37+
DescendToByte,
38+
DescendIndexedBranch,
39+
DescendFirstByte,
40+
DescendUntil,
41+
MoveToPath,
42+
AscendByte,
43+
Ascend,
44+
ToNextSiblingByte,
45+
ToPrevSiblingByte,
46+
ToNextStep,
47+
AscendUntil,
48+
AscendUntilBranch,
49+
ToNextVal,
50+
DescendFirstKPath,
51+
ToNextKPath,
52+
ToNextGetValue,
53+
ForkReadZipper,
54+
}
55+
}
56+
57+
pub struct Counter {
58+
pub count: AtomicU64,
59+
pub cycles: AtomicU64,
60+
}
61+
62+
impl Counter {
63+
const fn new() -> Self {
64+
Self {
65+
count: AtomicU64::new(0),
66+
cycles: AtomicU64::new(0),
67+
}
68+
}
69+
}
70+
71+
pub static COUNTERS: [Counter; Entries::COUNT] =
72+
[const { Counter::new() }; Entries::COUNT];
73+
74+
pub fn reset_counters() {
75+
for counter in &COUNTERS {
76+
counter.count.store(0, Ordering::Relaxed);
77+
counter.cycles.store(0, Ordering::Relaxed);
78+
}
79+
}
80+
81+
pub fn print_counters() {
82+
println!("{:>20},Count,TSCDelta,TSCAverage", "Name");
83+
for &entry in Entries::ALL {
84+
let counter = &COUNTERS[entry as usize];
85+
let count = counter.count.load(Ordering::Relaxed);
86+
let cycles = counter.cycles.load(Ordering::Relaxed);
87+
if count == 0 && cycles == 0 { continue; }
88+
let average = cycles as f64 / count as f64;
89+
println!("{:>20},{},{},{}", entry.to_str(), count, cycles, average);
90+
}
91+
}
92+
93+
#[cfg(target_arch = "aarch64")]
94+
#[inline]
95+
fn read_cycle_counter() -> u64 {
96+
let val: u64;
97+
unsafe {
98+
asm!(
99+
"mrs {}, CNTVCT_EL0",
100+
out(reg) val,
101+
options(nostack, nomem)
102+
);
103+
}
104+
val
105+
}
106+
107+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
108+
#[inline]
109+
fn read_cycle_counter() -> u64 {
110+
let lo: u32;
111+
let hi: u32;
112+
unsafe {
113+
asm!(
114+
"rdtsc",
115+
out("eax") lo,
116+
out("edx") hi,
117+
options(nostack, nomem)
118+
);
119+
}
120+
((hi as u64) << 32) | (lo as u64)
121+
}
122+
123+
pub struct TimedSpanGuard<'a> {
124+
start: u64,
125+
counter: &'a AtomicU64,
126+
}
127+
128+
impl<'a> TimedSpanGuard<'a> {
129+
pub fn new(counter: &'a AtomicU64) -> Self {
130+
Self {
131+
start: read_cycle_counter(),
132+
counter,
133+
}
134+
}
135+
}
136+
137+
impl<'a> Drop for TimedSpanGuard<'a> {
138+
fn drop(&mut self) {
139+
let end = read_cycle_counter();
140+
self.counter.fetch_add(end - self.start, Ordering::Relaxed);
141+
}
142+
}
143+
144+
pub const ENABLED: bool = true;
145+
146+
macro_rules! timed_span {
147+
($name:ident, $dst:path) => {
148+
let _guard = if $crate::timed_span::ENABLED {
149+
let index = $crate::timed_span::Entries::$name as usize;
150+
let counter = &$dst[index];
151+
counter.count.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
152+
Some($crate::timed_span::TimedSpanGuard::new(&counter.cycles))
153+
} else {
154+
None
155+
};
156+
};
157+
($name:ident) => {
158+
$crate::timed_span::timed_span!($name, $crate::timed_span::COUNTERS)
159+
};
160+
}
161+
162+
pub(crate) use timed_span;
163+
164+
#[cfg(test)]
165+
mod tests {
166+
use super::*;
167+
168+
#[test]
169+
fn test_timed_span() {
170+
reset_counters();
171+
{
172+
timed_span!(Reset);
173+
for ii in 0..100_000 {
174+
core::hint::black_box(ii);
175+
}
176+
}
177+
print_counters();
178+
}
179+
}

0 commit comments

Comments
 (0)