blob: 1dc76415e9fb89f1f1b9f06c0cc626383d9c9e5e [file] [log] [blame]
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_INTERSECTOR_H_
#define SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_INTERSECTOR_H_
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <memory>
#include <utility>
#include <vector>
#include "perfetto/base/logging.h"
#include "src/trace_processor/containers/interval_tree.h"
namespace perfetto::trace_processor {
// Provides functionality for efficient intersection of a set of intervals with
// another interval. Operates in various modes: using interval tree, binary
// search of non overlapping intervals or linear scan if the intervals are
// overlapping but there is no need for interval tree.
class IntervalIntersector {
public:
// Mode of intersection. Choosing the mode strongly impacts the performance
// of intersector.
enum Mode {
// Use `IntervalTree` as an underlying implementation.. Would create an
// interval tree - with complexity of O(N) and memory complexity of O(N).
// Query cost is O(logN).
// NOTE: Only use if intevals are overlapping and tree would be queried
// multiple times.
kIntervalTree,
// If the intervals are non overlapping we can use simple binary search.
// There is no memory cost and algorithmic complexity of O(logN + M), where
// logN is the cost of binary search and M is the number of results.
// NOTE: Only use if intervals are non overlapping.
kBinarySearch,
// Slightly better then linear scan, we are looking for the first
// overlapping interval and then doing linear scan of the rest. NOTE: Only
// use if intervals are overlapping and there would be very few queries.
kLinearScan
};
// Creates an interval tree from the vector of intervals if needed. Otherwise
// copies the vector of intervals.
explicit IntervalIntersector(const std::vector<Interval>& sorted_intervals,
Mode mode)
: intervals_(sorted_intervals), mode_(mode) {
if (sorted_intervals.empty()) {
mode_ = kBinarySearch;
return;
}
if (mode_ == kIntervalTree) {
tree = std::make_unique<IntervalTree>(intervals_);
}
}
// Modifies |res| to contain Interval::Id of all intervals that overlap
// interval (s, e).
template <typename T>
void FindOverlaps(uint64_t s, uint64_t e, std::vector<T>& res) const {
if (mode_ == kIntervalTree) {
tree->FindOverlaps(s, e, res);
return;
}
// Find the first interval that ends after |s|.
auto overlap =
std::lower_bound(intervals_.begin(), intervals_.end(), s,
[](const Interval& interval, uint64_t start) {
return interval.end <= start;
});
if (mode_ == kBinarySearch) {
for (; overlap != intervals_.end() && overlap->start < e; ++overlap) {
UpdateResultVector(s, e, *overlap, res);
}
return;
}
PERFETTO_CHECK(mode_ == kLinearScan);
for (; overlap != intervals_.end() && overlap->start < e; ++overlap) {
if (overlap->end <= s) {
continue;
}
if (overlap->start > s) {
break;
}
UpdateResultVector(s, e, *overlap, res);
}
for (; overlap != intervals_.end() && overlap->start < e; ++overlap) {
UpdateResultVector(s, e, *overlap, res);
}
}
// Helper function to decide which intersector mode would be in given
// situations. Only use if the number of queries is known.
static Mode DecideMode(bool is_nonoverlapping, uint32_t queries_count) {
if (is_nonoverlapping) {
return kBinarySearch;
}
if (queries_count < 5) {
return kLinearScan;
}
return kIntervalTree;
}
private:
void UpdateResultVector(uint64_t s,
uint64_t e,
const Interval& overlap,
std::vector<Interval>& res) const {
Interval new_int;
new_int.start = std::max(s, overlap.start);
new_int.end = std::min(e, overlap.end);
new_int.id = overlap.id;
res.push_back(new_int);
}
void UpdateResultVector(uint64_t,
uint64_t,
const Interval& overlap,
std::vector<Id>& res) const {
res.push_back(overlap.id);
}
const std::vector<Interval>& intervals_;
Mode mode_;
// If |use_interval_tree_|.
std::unique_ptr<IntervalTree> tree;
};
} // namespace perfetto::trace_processor
#endif // SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_INTERSECTOR_H_