Skip to main content

turbo_trace_server/
self_time_tree.rs

1use std::mem::take;
2
3use crate::timestamp::Timestamp;
4
5const SPLIT_COUNT: usize = 128;
6/// Start balancing the tree when there are N times more items on one side. Must be at least 3.
7const BALANCE_THRESHOLD: usize = 3;
8
9pub struct SelfTimeTree<T> {
10    entries: Vec<SelfTimeEntry<T>>,
11    children: Option<Box<SelfTimeChildren<T>>>,
12    count: usize,
13}
14
15struct SelfTimeEntry<T> {
16    start: Timestamp,
17    end: Timestamp,
18    item: T,
19}
20
21struct SelfTimeChildren<T> {
22    /// Entries < split_point
23    left: SelfTimeTree<T>,
24    split_point: Timestamp,
25    /// Entries >= split_point
26    right: SelfTimeTree<T>,
27    /// Number of entries in the SelfTimeTree::entries list that overlap the split point
28    spanning_entries: usize,
29}
30
31impl<T> Default for SelfTimeTree<T> {
32    fn default() -> Self {
33        Self {
34            entries: Vec::new(),
35            children: None,
36            count: 0,
37        }
38    }
39}
40
41impl<T> SelfTimeTree<T> {
42    pub fn new() -> Self {
43        Self::default()
44    }
45
46    pub fn len(&self) -> usize {
47        self.count
48    }
49
50    pub fn insert(&mut self, start: Timestamp, end: Timestamp, item: T) {
51        self.count += 1;
52        self.entries.push(SelfTimeEntry { start, end, item });
53        self.check_for_split();
54    }
55
56    fn insert_without_check(&mut self, start: Timestamp, end: Timestamp, item: T) {
57        self.count += 1;
58        self.entries.push(SelfTimeEntry { start, end, item });
59    }
60
61    fn check_for_split(&mut self) {
62        if self.entries.len() >= SPLIT_COUNT {
63            let spanning_entries = if let Some(children) = &mut self.children {
64                children.spanning_entries
65            } else {
66                0
67            };
68            if self.entries.len() - spanning_entries >= SPLIT_COUNT {
69                self.split();
70            }
71        }
72    }
73
74    pub fn optimize(&mut self) {
75        if self.children.is_some() {
76            self.distribute_entries();
77            self.rebalance();
78            let children = self.children.as_mut().unwrap();
79            children.left.optimize();
80            children.right.optimize();
81        }
82        self.entries.shrink_to_fit();
83    }
84
85    fn split(&mut self) {
86        debug_assert!(!self.entries.is_empty());
87        self.distribute_entries();
88        self.rebalance();
89    }
90
91    fn distribute_entries(&mut self) {
92        if self.children.is_none() {
93            let (start, end) = self
94                .entries
95                .iter()
96                .fold((Timestamp::MAX, Timestamp::ZERO), |(lo, hi), e| {
97                    (lo.min(e.start), hi.max(e.end))
98                });
99            let middle = (start + end) / 2;
100            // Pre-allocate half the split threshold: after distributing, each child
101            // typically receives ~SPLIT_COUNT / 2 entries.
102            self.children = Some(Box::new(SelfTimeChildren {
103                left: SelfTimeTree {
104                    entries: Vec::with_capacity(SPLIT_COUNT / 2),
105                    ..SelfTimeTree::default()
106                },
107                split_point: middle,
108                right: SelfTimeTree {
109                    entries: Vec::with_capacity(SPLIT_COUNT / 2),
110                    ..SelfTimeTree::default()
111                },
112                spanning_entries: 0,
113            }));
114        }
115        let Some(children) = &mut self.children else {
116            unreachable!();
117        };
118        let mut i = children.spanning_entries;
119        while i < self.entries.len() {
120            let SelfTimeEntry { start, end, .. } = self.entries[i];
121            if end <= children.split_point {
122                let SelfTimeEntry { start, end, item } = self.entries.swap_remove(i);
123                children.left.insert_without_check(start, end, item);
124            } else if start >= children.split_point {
125                let SelfTimeEntry { start, end, item } = self.entries.swap_remove(i);
126                children.right.insert_without_check(start, end, item);
127            } else {
128                self.entries.swap(i, children.spanning_entries);
129                children.spanning_entries += 1;
130                i += 1;
131            }
132        }
133        children.left.check_for_split();
134        children.right.check_for_split();
135    }
136
137    fn rebalance(&mut self) {
138        if let Some(box SelfTimeChildren {
139            left,
140            split_point,
141            right,
142            spanning_entries,
143        }) = &mut self.children
144        {
145            let SelfTimeTree {
146                count: left_count,
147                children: left_children,
148                entries: left_entries,
149            } = left;
150            let SelfTimeTree {
151                count: right_count,
152                children: right_children,
153                entries: right_entries,
154            } = right;
155            if *left_count > *right_count * BALANCE_THRESHOLD + *spanning_entries {
156                // The left side has overweight
157                // We want to have a new tree that is:
158                // left' = left.left
159                // right' = (left.right, right) with self.split_point
160                // split_point' = left.split_point
161                // direct entries in self and left are put in self and are redistributed
162                if let Some(box SelfTimeChildren {
163                    left: left_left,
164                    split_point: left_split_point,
165                    right: left_right,
166                    spanning_entries: _,
167                }) = left_children
168                {
169                    *right = Self {
170                        count: left_right.count + right.count,
171                        entries: Vec::new(),
172                        children: Some(Box::new(SelfTimeChildren {
173                            left: take(left_right),
174                            split_point: *split_point,
175                            right: take(right),
176                            spanning_entries: 0,
177                        })),
178                    };
179                    *split_point = *left_split_point;
180                    self.entries.append(left_entries);
181                    *left = take(left_left);
182                    *spanning_entries = 0;
183                    self.distribute_entries();
184                }
185            } else if *right_count > *left_count * BALANCE_THRESHOLD + *spanning_entries {
186                // The right side has overweight
187                // We want to have a new tree that is:
188                // left' = (left, right.left) with self.split_point
189                // right' = right.right
190                // split_point' = right.split_point
191                // direct entries in self and right are put in self and are redistributed
192                if let Some(box SelfTimeChildren {
193                    left: right_left,
194                    split_point: right_split_point,
195                    right: right_right,
196                    spanning_entries: _,
197                }) = right_children
198                {
199                    *left = Self {
200                        count: left.count + right_left.count,
201                        entries: Vec::new(),
202                        children: Some(Box::new(SelfTimeChildren {
203                            left: take(left),
204                            split_point: *split_point,
205                            right: take(right_left),
206                            spanning_entries: 0,
207                        })),
208                    };
209                    *split_point = *right_split_point;
210                    self.entries.append(right_entries);
211                    *right = take(right_right);
212                    *spanning_entries = 0;
213                    self.distribute_entries();
214                }
215            }
216        }
217    }
218
219    #[cfg(test)]
220    pub fn lookup_range_count(&self, start: Timestamp, end: Timestamp) -> Timestamp {
221        let mut total_count = Timestamp::ZERO;
222        for entry in &self.entries {
223            if entry.start <= end && entry.end >= start {
224                let start = std::cmp::max(entry.start, start);
225                let end = std::cmp::min(entry.end, end);
226                let span = end - start;
227                total_count += span;
228            }
229        }
230        if let Some(children) = &self.children {
231            if start <= children.split_point {
232                total_count += children.left.lookup_range_count(start, end);
233            }
234            if end >= children.split_point {
235                total_count += children.right.lookup_range_count(start, end);
236            }
237        }
238        total_count
239    }
240
241    pub fn lookup_range_corrected_time(&self, start: Timestamp, end: Timestamp) -> Timestamp {
242        #[derive(PartialEq, Eq, PartialOrd, Ord)]
243        enum Change {
244            Start,
245            End,
246        }
247        let mut current_count = 0;
248        let mut changes = Vec::new();
249        self.for_each_in_range(start, end, &mut |s, e, _| {
250            if s <= start {
251                current_count += 1;
252            } else {
253                changes.push((s, Change::Start));
254            }
255            if e < end {
256                changes.push((e, Change::End));
257            }
258        });
259
260        // Fast path: every overlapping interval fully contains `[start, end]`, so the
261        // count is constant over the whole window. Skip the sort and the sweep.
262        if changes.is_empty() {
263            if current_count == 0 {
264                return Timestamp::ZERO;
265            }
266            return Timestamp::from_value(*(end - start) / current_count);
267        }
268
269        changes.sort_unstable();
270        let mut factor_times_1000 = 0u64;
271        let mut current_ts = start;
272        for (ts, change) in changes {
273            if current_ts < ts {
274                // Move time
275                let time_diff = ts - current_ts;
276                factor_times_1000 += *time_diff * 1000 / current_count;
277                current_ts = ts;
278            }
279            match change {
280                Change::Start => current_count += 1,
281                Change::End => current_count -= 1,
282            }
283        }
284        if current_ts < end {
285            let time_diff = end - current_ts;
286            factor_times_1000 += *time_diff * 1000 / current_count;
287        }
288        Timestamp::from_value(factor_times_1000 / 1000)
289    }
290
291    pub fn for_each_in_range(
292        &self,
293        start: Timestamp,
294        end: Timestamp,
295        f: &mut impl FnMut(Timestamp, Timestamp, &T),
296    ) {
297        for entry in &self.entries {
298            if entry.start <= end && entry.end >= start {
299                f(entry.start, entry.end, &entry.item);
300            }
301        }
302        if let Some(children) = &self.children {
303            if start <= children.split_point {
304                children.left.for_each_in_range(start, end, f);
305            }
306            if end >= children.split_point {
307                children.right.for_each_in_range(start, end, f);
308            }
309        }
310    }
311
312    pub fn for_each_in_range_optimize(
313        &mut self,
314        start: Timestamp,
315        end: Timestamp,
316        f: &mut impl FnMut(Timestamp, Timestamp, &T),
317    ) {
318        if self.children.is_some() {
319            self.distribute_entries();
320            self.rebalance();
321        }
322        for entry in &self.entries {
323            if entry.start <= end && entry.end >= start {
324                f(entry.start, entry.end, &entry.item);
325            }
326        }
327        if let Some(children) = &mut self.children {
328            if start <= children.split_point {
329                children.left.for_each_in_range_optimize(start, end, f);
330            }
331            if end >= children.split_point {
332                children.right.for_each_in_range_optimize(start, end, f);
333            }
334        }
335    }
336}
337
338#[cfg(test)]
339mod tests {
340    use super::*;
341
342    fn print_tree<T>(tree: &SelfTimeTree<T>, indent: usize) {
343        if let Some(children) = &tree.children {
344            println!(
345                "{}{} items (split at {}, {} overlapping, {} total)",
346                " ".repeat(indent),
347                tree.entries.len(),
348                children.split_point,
349                children.spanning_entries,
350                tree.count
351            );
352            print_tree(&children.left, indent + 2);
353            print_tree(&children.right, indent + 2);
354        } else {
355            println!(
356                "{}{} items ({} total)",
357                " ".repeat(indent),
358                tree.entries.len(),
359                tree.count
360            );
361        }
362    }
363
364    fn assert_balanced<T>(tree: &SelfTimeTree<T>) {
365        if let Some(children) = &tree.children {
366            let l = children.left.count;
367            let r = children.right.count;
368            let s = children.spanning_entries;
369            if (l > SPLIT_COUNT || r > SPLIT_COUNT)
370                && ((l > r * BALANCE_THRESHOLD + s) || (r > l * BALANCE_THRESHOLD + s))
371            {
372                print_tree(tree, 0);
373                panic!("Tree is not balanced");
374            }
375            assert_balanced(&children.left);
376            assert_balanced(&children.right);
377        }
378    }
379
380    #[test]
381    fn test_simple() {
382        let mut tree = SelfTimeTree::new();
383        let count = 10000;
384        for i in 0..count {
385            tree.insert(Timestamp::from_micros(i), Timestamp::from_micros(i + 1), i);
386            assert_eq!(tree.count, (i + 1) as usize);
387            assert_balanced(&tree);
388        }
389        assert_eq!(
390            tree.lookup_range_count(Timestamp::ZERO, Timestamp::from_micros(count)),
391            Timestamp::from_micros(count)
392        );
393        print_tree(&tree, 0);
394        assert_balanced(&tree);
395    }
396
397    #[test]
398    fn test_evenly() {
399        let mut tree = SelfTimeTree::new();
400        let count = 10000;
401        for a in 0..10 {
402            for b in 0..10 {
403                for c in 0..10 {
404                    for d in 0..10 {
405                        let i = d * 1000 + c * 100 + b * 10 + a;
406                        tree.insert(Timestamp::from_micros(i), Timestamp::from_micros(i + 1), i);
407                        assert_balanced(&tree);
408                    }
409                }
410            }
411        }
412        assert_eq!(
413            tree.lookup_range_count(Timestamp::ZERO, Timestamp::from_micros(count)),
414            Timestamp::from_micros(count)
415        );
416        print_tree(&tree, 0);
417        assert_balanced(&tree);
418    }
419
420    #[test]
421    fn test_overlapping() {
422        let mut tree = SelfTimeTree::new();
423        let count = 10000;
424        for i in 0..count {
425            tree.insert(
426                Timestamp::from_micros(i),
427                Timestamp::from_micros(i + 100),
428                i,
429            );
430            assert_eq!(tree.count, (i + 1) as usize);
431            assert_balanced(&tree);
432        }
433        assert_eq!(
434            tree.lookup_range_count(Timestamp::ZERO, Timestamp::from_micros(count + 100)),
435            Timestamp::from_micros(count * 100)
436        );
437        print_tree(&tree, 0);
438        assert_balanced(&tree);
439    }
440
441    #[test]
442    fn test_overlapping_heavy() {
443        let mut tree = SelfTimeTree::new();
444        let count = 10000;
445        for i in 0..count {
446            tree.insert(
447                Timestamp::from_micros(i),
448                Timestamp::from_micros(i + 500),
449                i,
450            );
451            assert_eq!(tree.count, (i + 1) as usize);
452        }
453        assert_eq!(
454            tree.lookup_range_count(Timestamp::ZERO, Timestamp::from_micros(count + 500)),
455            Timestamp::from_micros(count * 500)
456        );
457        print_tree(&tree, 0);
458        assert_balanced(&tree);
459    }
460
461    #[test]
462    fn test_corrected_time_no_overlap() {
463        // No intervals at all — corrected time of an empty range is zero.
464        let tree: SelfTimeTree<u32> = SelfTimeTree::new();
465        let r = tree
466            .lookup_range_corrected_time(Timestamp::from_micros(0), Timestamp::from_micros(100));
467        assert_eq!(r, Timestamp::ZERO);
468    }
469
470    #[test]
471    fn test_corrected_time_single_interval() {
472        // One interval matching the query window exactly: correction factor 1, returns full
473        // duration.
474        let mut tree = SelfTimeTree::new();
475        tree.insert(Timestamp::from_micros(0), Timestamp::from_micros(100), 0u32);
476        let r = tree
477            .lookup_range_corrected_time(Timestamp::from_micros(0), Timestamp::from_micros(100));
478        assert_eq!(r, Timestamp::from_micros(100));
479    }
480
481    #[test]
482    fn test_corrected_time_fast_path_full_containment() {
483        // Two intervals each fully contain [10, 20]: count is 2 throughout, answer = 10/2 = 5us.
484        let mut tree = SelfTimeTree::new();
485        tree.insert(Timestamp::from_micros(0), Timestamp::from_micros(100), 0u32);
486        tree.insert(Timestamp::from_micros(5), Timestamp::from_micros(50), 1u32);
487        let r = tree
488            .lookup_range_corrected_time(Timestamp::from_micros(10), Timestamp::from_micros(20));
489        assert_eq!(r, Timestamp::from_micros(5));
490    }
491
492    #[test]
493    fn test_corrected_time_partial_overlap() {
494        // [0, 100] is the query window. Intervals:
495        //   A: [0, 100]    (covers whole window)
496        //   B: [30, 70]    (fully inside, contributes corrections during [30, 70])
497        // Expected:
498        //   [0, 30):  count=1, time=30, corrected = 30 / 1 = 30
499        //   [30, 70): count=2, time=40, corrected = 40 / 2 = 20
500        //   [70, 100): count=1, time=30, corrected = 30 / 1 = 30
501        // Total: 80us
502        let mut tree = SelfTimeTree::new();
503        tree.insert(Timestamp::from_micros(0), Timestamp::from_micros(100), 0u32);
504        tree.insert(Timestamp::from_micros(30), Timestamp::from_micros(70), 1u32);
505        let r = tree
506            .lookup_range_corrected_time(Timestamp::from_micros(0), Timestamp::from_micros(100));
507        assert_eq!(r, Timestamp::from_micros(80));
508    }
509}