blob: 1c70d0568fa7e071d734dba5a5fb622dd1542182 [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_TREE_H_
#define SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_TREE_H_
#include <algorithm>
#include <cstdint>
#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <vector>
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/small_vector.h"
namespace perfetto::trace_processor {
using Ts = uint64_t;
using Id = uint32_t;
// An implementation of a centered interval tree data structure, designed to
// efficiently find all overlap queries on a set of intervals. Centered interval
// tree has a build complexity of O(N*logN) and a query time of O(logN + k),
// where k is the number of overlaps in the dataset.
class IntervalTree {
public:
// Maps to one trace processor slice.
struct Interval {
Ts start;
Ts end;
Id id;
};
// Creates an interval tree from the vector of intervals.
explicit IntervalTree(const std::vector<Interval>& sorted_intervals) {
nodes_.reserve(sorted_intervals.size());
Node root_node(sorted_intervals.data(), sorted_intervals.size(), nodes_);
nodes_.emplace_back(std::move(root_node));
root_ = nodes_.size() - 1;
}
// Modifies |res| to contain Interval::Id of all intervals that overlap
// interval (s, e). Has a complexity of O(log(size of tree) + (number of
// overlaps)).
void FindOverlaps(uint64_t s, uint64_t e, std::vector<Id>& res) const {
std::vector<const Node*> stack{nodes_.data() + root_};
while (!stack.empty()) {
const Node* n = stack.back();
stack.pop_back();
for (const Interval& i : n->intervals_) {
// As we know that each interval overlaps the center, if the interval
// starts after the |end| we know [start,end] can't intersect the
// center.
if (i.start > e) {
break;
}
if (e > i.start && s < i.end) {
res.push_back(i.id);
}
}
if (e > n->center_ &&
n->right_node_ != std::numeric_limits<size_t>::max()) {
stack.push_back(&nodes_[n->right_node_]);
}
if (s < n->center_ &&
n->left_node_ != std::numeric_limits<size_t>::max()) {
stack.push_back(&nodes_[n->left_node_]);
}
}
}
// Modifies |res| to contain all overlaps (as Intervals) that overlap interval
// (s, e). Has a complexity of O(log(size of tree) + (number of overlaps)).
void FindOverlaps(Ts s, Ts e, std::vector<Interval>& res) const {
std::vector<const Node*> stack{nodes_.data() + root_};
while (!stack.empty()) {
const Node* n = stack.back();
stack.pop_back();
for (const Interval& i : n->intervals_) {
// As we know that each interval overlaps the center, if the interval
// starts after the |end| we know [start,end] can't intersect the
// center.
if (i.start > e) {
break;
}
if (e > i.start && s < i.end) {
Interval new_int;
new_int.start = std::max(s, i.start);
new_int.end = std::min(e, i.end);
new_int.id = i.id;
res.push_back(new_int);
}
}
if (e > n->center_ &&
n->right_node_ != std::numeric_limits<size_t>::max()) {
stack.push_back(&nodes_[n->right_node_]);
}
if (s < n->center_ &&
n->left_node_ != std::numeric_limits<size_t>::max()) {
stack.push_back(&nodes_[n->left_node_]);
}
}
}
private:
struct Node {
base::SmallVector<Interval, 2> intervals_;
uint64_t center_ = 0;
size_t left_node_ = std::numeric_limits<size_t>::max();
size_t right_node_ = std::numeric_limits<size_t>::max();
explicit Node(const Interval* intervals,
size_t intervals_size,
std::vector<Node>& nodes) {
const Interval& mid_interval = intervals[intervals_size / 2];
center_ = (mid_interval.start + mid_interval.end) / 2;
// Find intervals that overlap the center_ and intervals that belong to
// the left node (finish before the center_). If an interval starts after
// the center break and assign all remining intervals to the right node.
// We can do this as the provided intervals are in sorted order.
std::vector<Interval> left;
for (uint32_t i = 0; i < intervals_size; i++) {
const Interval& inter = intervals[i];
// Starts after the center. As intervals are sorted on timestamp we
// know the rest of intervals will go to the right node.
if (inter.start > center_) {
Node n(intervals + i, intervals_size - i, nodes);
nodes.emplace_back(std::move(n));
right_node_ = nodes.size() - 1;
break;
}
// Finishes before the center.
if (inter.end < center_) {
left.push_back(intervals[i]);
} else {
// Overlaps the center.
intervals_.emplace_back(intervals[i]);
}
}
if (!left.empty()) {
Node n(left.data(), left.size(), nodes);
nodes.emplace_back(std::move(n));
left_node_ = nodes.size() - 1;
}
}
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
Node(Node&&) = default;
Node& operator=(Node&&) = default;
};
size_t root_;
std::vector<Node> nodes_;
};
} // namespace perfetto::trace_processor
#endif // SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_TREE_H_