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