• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_INTERSECTOR_H_
18 #define SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_INTERSECTOR_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 "src/trace_processor/containers/interval_tree.h"
30 
31 namespace perfetto::trace_processor {
32 
33 // Provides functionality for efficient intersection of a set of intervals with
34 // another interval. Operates in various modes: using interval tree, binary
35 // search of non overlapping intervals or linear scan if the intervals are
36 // overlapping but there is no need for interval tree.
37 class IntervalIntersector {
38  public:
39   // Mode of intersection. Choosing the mode strongly impacts the performance
40   // of intersector.
41   enum Mode {
42     // Use `IntervalTree` as an underlying implementation.. Would create an
43     // interval tree - with complexity of O(N) and memory complexity of O(N).
44     // Query cost is O(logN).
45     // NOTE: Only use if intevals are overlapping and tree would be queried
46     // multiple times.
47     kIntervalTree,
48     // If the intervals are non overlapping we can use simple binary search.
49     // There is no memory cost and algorithmic complexity of O(logN + M), where
50     // logN is the cost of binary search and M is the number of results.
51     // NOTE: Only use if intervals are non overlapping.
52     kBinarySearch,
53     // Slightly better then linear scan, we are looking for the first
54     // overlapping interval and then doing linear scan of the rest. NOTE: Only
55     // use if intervals are overlapping and there would be very few queries.
56     kLinearScan
57   };
58 
59   // Creates an interval tree from the vector of intervals if needed. Otherwise
60   // copies the vector of intervals.
IntervalIntersector(const std::vector<Interval> & sorted_intervals,Mode mode)61   explicit IntervalIntersector(const std::vector<Interval>& sorted_intervals,
62                                Mode mode)
63       : intervals_(sorted_intervals), mode_(mode) {
64     if (sorted_intervals.empty()) {
65       mode_ = kBinarySearch;
66       return;
67     }
68     if (mode_ == kIntervalTree) {
69       tree = std::make_unique<IntervalTree>(intervals_);
70     }
71   }
72 
73   // Modifies |res| to contain Interval::Id of all intervals that overlap
74   // interval (s, e).
75   template <typename T>
FindOverlaps(uint64_t s,uint64_t e,std::vector<T> & res)76   void FindOverlaps(uint64_t s, uint64_t e, std::vector<T>& res) const {
77     if (mode_ == kIntervalTree) {
78       tree->FindOverlaps(s, e, res);
79       return;
80     }
81 
82     if (mode_ == kBinarySearch) {
83       // Find the first interval that ends after |s|.
84       auto overlap =
85           std::lower_bound(intervals_.begin(), intervals_.end(), s,
86                            [](const Interval& interval, uint64_t start) {
87                              return interval.end <= start;
88                            });
89 
90       for (; overlap != intervals_.end() && overlap->start < e; ++overlap) {
91         UpdateResultVector(s, e, *overlap, res);
92       }
93       return;
94     }
95 
96     // When using linear scan, we know only that the that if interval starts
97     // after the |e|, it will not overlap. We need to go through all intervals
98     // up to this point, as we don't know if any of the previous one is not
99     // overlapping.
100     PERFETTO_CHECK(mode_ == kLinearScan);
101 
102     auto cur_interval = intervals_.begin();
103 
104     // Go through all intervals that start before |s|.
105     for (; cur_interval != intervals_.end(); ++cur_interval) {
106       // An interval that ends before |s| can't overlap.
107       if (cur_interval->end <= s) {
108         continue;
109       }
110 
111       // Escape if the interval starts after |s|.
112       if (cur_interval->start > s) {
113         break;
114       }
115 
116       UpdateResultVector(s, e, *cur_interval, res);
117     }
118 
119     // Go through all intervals that start after |s| and before |e|.
120     for (; cur_interval != intervals_.end() && cur_interval->start < e;
121          ++cur_interval) {
122       UpdateResultVector(s, e, *cur_interval, res);
123     }
124   }
125 
126   // Helper function to decide which intersector mode would be in given
127   // situations. Only use if the number of queries is known.
DecideMode(bool is_nonoverlapping,uint32_t queries_count)128   static Mode DecideMode(bool is_nonoverlapping, uint32_t queries_count) {
129     if (is_nonoverlapping) {
130       return kBinarySearch;
131     }
132     if (queries_count < 5) {
133       return kLinearScan;
134     }
135     return kIntervalTree;
136   }
137 
138  private:
UpdateResultVector(uint64_t s,uint64_t e,const Interval & overlap,std::vector<Interval> & res)139   void UpdateResultVector(uint64_t s,
140                           uint64_t e,
141                           const Interval& overlap,
142                           std::vector<Interval>& res) const {
143     Interval new_int;
144     new_int.start = std::max(s, overlap.start);
145     new_int.end = std::min(e, overlap.end);
146     new_int.id = overlap.id;
147     res.push_back(new_int);
148   }
149 
UpdateResultVector(uint64_t,uint64_t,const Interval & overlap,std::vector<Id> & res)150   void UpdateResultVector(uint64_t,
151                           uint64_t,
152                           const Interval& overlap,
153 
154                           std::vector<Id>& res) const {
155     res.push_back(overlap.id);
156   }
157   const std::vector<Interval>& intervals_;
158   Mode mode_;
159 
160   // If |use_interval_tree_|.
161   std::unique_ptr<IntervalTree> tree;
162 };
163 
164 }  // namespace perfetto::trace_processor
165 
166 #endif  // SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_INTERSECTOR_H_
167