xref: /aosp_15_r20/external/perfetto/src/trace_processor/containers/interval_tree.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_TREE_H_
18 #define SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_TREE_H_
19 
20 #include <algorithm>
21 #include <cstddef>
22 #include <cstdint>
23 #include <limits>
24 #include <memory>
25 #include <utility>
26 #include <vector>
27 
28 #include "perfetto/base/logging.h"
29 #include "perfetto/ext/base/small_vector.h"
30 
31 namespace perfetto::trace_processor {
32 
33 using Ts = uint64_t;
34 using Id = uint32_t;
35 
36 struct Interval {
37   Ts start;
38   Ts end;
39   Id id;
40 };
41 
42 // An implementation of a centered interval tree data structure, designed to
43 // efficiently find all overlap queries on a set of intervals. Centered interval
44 // tree has a build complexity of O(N*logN) and a query time of O(logN + k),
45 // where k is the number of overlaps in the dataset.
46 class IntervalTree {
47  public:
48   // Creates an interval tree from the vector of intervals if needed. Otherwise
49   // copies the vector of intervals.
IntervalTree(const std::vector<Interval> & sorted_intervals)50   explicit IntervalTree(const std::vector<Interval>& sorted_intervals) {
51     PERFETTO_CHECK(!sorted_intervals.empty());
52     nodes_.reserve(sorted_intervals.size());
53     Node root_node(sorted_intervals.data(), sorted_intervals.size(), nodes_);
54     nodes_.emplace_back(std::move(root_node));
55     root_ = nodes_.size() - 1;
56   }
57 
58   // Modifies |res| to contain Interval::Id of all intervals that overlap
59   // interval (s, e). Has a complexity of O(log(size of tree) + (number of
60   // overlaps)).
FindOverlaps(uint64_t s,uint64_t e,std::vector<Id> & res)61   void FindOverlaps(uint64_t s, uint64_t e, std::vector<Id>& res) const {
62     std::vector<const Node*> stack{nodes_.data() + root_};
63     while (!stack.empty()) {
64       const Node* n = stack.back();
65       stack.pop_back();
66 
67       for (const Interval& i : n->intervals_) {
68         // As we know that each interval overlaps the center, if the interval
69         // starts after the |end| we know [start,end] can't intersect the
70         // center.
71         if (i.start > e) {
72           break;
73         }
74 
75         if (e > i.start && s < i.end) {
76           res.push_back(i.id);
77         }
78       }
79 
80       if (e > n->center_ &&
81           n->right_node_ != std::numeric_limits<size_t>::max()) {
82         stack.push_back(&nodes_[n->right_node_]);
83       }
84       if (s < n->center_ &&
85           n->left_node_ != std::numeric_limits<size_t>::max()) {
86         stack.push_back(&nodes_[n->left_node_]);
87       }
88     }
89   }
90 
91   // Modifies |res| to contain all overlaps (as Intervals) that overlap interval
92   // (s, e). Has a complexity of O(log(size of tree) + (number of overlaps)).
FindOverlaps(Ts s,Ts e,std::vector<Interval> & res)93   void FindOverlaps(Ts s, Ts e, std::vector<Interval>& res) const {
94     std::vector<const Node*> stack{nodes_.data() + root_};
95     while (!stack.empty()) {
96       const Node* n = stack.back();
97       stack.pop_back();
98 
99       for (const Interval& i : n->intervals_) {
100         // As we know that each interval overlaps the center, if the interval
101         // starts after the |end| we know [start,end] can't intersect the
102         // center.
103         if (i.start > e) {
104           break;
105         }
106 
107         if (e > i.start && s < i.end) {
108           Interval new_int;
109           new_int.start = std::max(s, i.start);
110           new_int.end = std::min(e, i.end);
111           new_int.id = i.id;
112           res.push_back(new_int);
113         }
114       }
115 
116       if (e > n->center_ &&
117           n->right_node_ != std::numeric_limits<size_t>::max()) {
118         stack.push_back(&nodes_[n->right_node_]);
119       }
120       if (s < n->center_ &&
121           n->left_node_ != std::numeric_limits<size_t>::max()) {
122         stack.push_back(&nodes_[n->left_node_]);
123       }
124     }
125   }
126 
127  private:
128   struct Node {
129     base::SmallVector<Interval, 2> intervals_;
130     uint64_t center_ = 0;
131     size_t left_node_ = std::numeric_limits<size_t>::max();
132     size_t right_node_ = std::numeric_limits<size_t>::max();
133 
NodeNode134     explicit Node(const Interval* intervals,
135                   size_t intervals_size,
136                   std::vector<Node>& nodes) {
137       const Interval& mid_interval = intervals[intervals_size / 2];
138       center_ = (mid_interval.start + mid_interval.end) / 2;
139 
140       // Find intervals that overlap the center_ and intervals that belong to
141       // the left node (finish before the center_). If an interval starts after
142       // the center break and assign all remining intervals to the right node.
143       // We can do this as the provided intervals are in sorted order.
144       std::vector<Interval> left;
145       for (uint32_t i = 0; i < intervals_size; i++) {
146         const Interval& inter = intervals[i];
147         // Starts after the center. As intervals are sorted on timestamp we
148         // know the rest of intervals will go to the right node.
149         if (inter.start > center_) {
150           Node n(intervals + i, intervals_size - i, nodes);
151           nodes.emplace_back(std::move(n));
152           right_node_ = nodes.size() - 1;
153           break;
154         }
155 
156         // Finishes before the center.
157         if (inter.end < center_) {
158           left.push_back(intervals[i]);
159         } else {
160           // Overlaps the center.
161           intervals_.emplace_back(intervals[i]);
162         }
163       }
164 
165       if (!left.empty()) {
166         Node n(left.data(), left.size(), nodes);
167         nodes.emplace_back(std::move(n));
168         left_node_ = nodes.size() - 1;
169       }
170     }
171 
172     Node(const Node&) = delete;
173     Node& operator=(const Node&) = delete;
174 
175     Node(Node&&) = default;
176     Node& operator=(Node&&) = default;
177   };
178 
179   size_t root_;
180   std::vector<Node> nodes_;
181 };
182 }  // namespace perfetto::trace_processor
183 
184 #endif  // SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_TREE_H_
185