blob: 5855b44e2f8bcb5324d2237bb079ab8c3747767f [file] [log] [blame]
/*
* Copyright (C) 2019 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.
*/
#include "src/trace_processor/containers/row_map.h"
#include <algorithm>
#include <cstdint>
#include <unordered_set>
#include <utility>
#include <variant>
#include <vector>
#include "perfetto/base/logging.h"
#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map_algorithms.h"
namespace perfetto {
namespace trace_processor {
namespace {
using Range = RowMap::Range;
using OutputIndex = RowMap::OutputIndex;
using Variant = std::variant<Range, BitVector, std::vector<OutputIndex>>;
RowMap Select(Range range, Range selector) {
PERFETTO_DCHECK(selector.start <= selector.end);
PERFETTO_DCHECK(selector.end <= range.size());
return RowMap(range.start + selector.start, range.start + selector.end);
}
RowMap Select(Range range, const BitVector& selector) {
PERFETTO_DCHECK(selector.size() <= range.size());
// If |start| == 0 and |selector.size()| <= |end - start| (which is a
// precondition for this function), the BitVector we generate is going to be
// exactly |selector|.
//
// This is a fast path for the common situation where, post-filtering,
// SelectRows is called on all the table RowMaps with a BitVector. The self
// RowMap will always be a range so we expect this case to be hit at least
// once every filter operation.
if (range.start == 0u)
return RowMap(selector.Copy());
// We only need to resize to |start| + |selector.size()| as we know any rows
// not covered by |selector| are going to be removed below.
BitVector bv(range.start, false);
bv.Resize(range.start + selector.size(), true);
bv.UpdateSetBits(selector);
return RowMap(std::move(bv));
}
RowMap Select(Range range, const std::vector<OutputIndex>& selector) {
std::vector<uint32_t> iv(selector.size());
for (uint32_t i = 0; i < selector.size(); ++i) {
PERFETTO_DCHECK(selector[i] < range.size());
iv[i] = selector[i] + range.start;
}
return RowMap(std::move(iv));
}
RowMap Select(const BitVector& bv, Range selector) {
PERFETTO_DCHECK(selector.end <= bv.CountSetBits());
if (selector.empty()) {
return {};
}
// If we're simply selecting every element in the bitvector, just
// return a copy of the BitVector without iterating.
if (selector.start == 0 && selector.end == bv.CountSetBits()) {
return RowMap(bv.Copy());
}
return RowMap(bv.IntersectRange(bv.IndexOfNthSet(selector.start),
bv.IndexOfNthSet(selector.end - 1) + 1));
}
RowMap Select(const BitVector& bv, const BitVector& selector) {
BitVector ret = bv.Copy();
ret.UpdateSetBits(selector);
return RowMap(std::move(ret));
}
RowMap Select(const BitVector& bv, const std::vector<uint32_t>& selector) {
// The value of this constant was found by considering the benchmarks
// |BM_SelectBvWithIvByConvertToIv| and |BM_SelectBvWithIvByIndexOfNthSet|.
//
// We use this to find the ratio between |bv.CountSetBits()| and
// |selector.size()| where |SelectBvWithIvByIndexOfNthSet| was found to be
// faster than |SelectBvWithIvByConvertToIv|.
//
// Note: as of writing this, the benchmarks do not take into account the fill
// ratio of the BitVector; they assume 50% rate which almost never happens in
// practice. In the future, we could also take this into account (by
// considering the ratio between bv.size() and bv.CountSetBits()) but this
// causes an explosion in the state space for the benchmark so we're not
// considering this today.
//
// The current value of the constant was picked by running these benchmarks on
// a E5-2690 v4 and finding the crossover point using a spreadsheet.
constexpr uint32_t kIndexOfSetBitToSelectorRatio = 4;
// If the selector is larger than a threshold, it's more efficient to convert
// the entire BitVector to an index vector and use SelectIvWithIv instead.
if (bv.CountSetBits() / kIndexOfSetBitToSelectorRatio < selector.size()) {
return RowMap(
row_map_algorithms::SelectBvWithIvByConvertToIv(bv, selector));
}
return RowMap(
row_map_algorithms::SelectBvWithIvByIndexOfNthSet(bv, selector));
}
RowMap Select(const std::vector<uint32_t>& iv, Range selector) {
PERFETTO_DCHECK(selector.end <= iv.size());
std::vector<uint32_t> ret(selector.size());
for (uint32_t i = selector.start; i < selector.end; ++i) {
ret[i - selector.start] = iv[i];
}
return RowMap(std::move(ret));
}
RowMap Select(const std::vector<uint32_t>& iv, const BitVector& selector) {
PERFETTO_DCHECK(selector.size() <= iv.size());
std::vector<uint32_t> copy = iv;
copy.resize(selector.size());
uint32_t idx = 0;
auto it = std::remove_if(
copy.begin(), copy.end(),
[&idx, &selector](uint32_t) { return !selector.IsSet(idx++); });
copy.erase(it, copy.end());
return RowMap(std::move(copy));
}
RowMap Select(const std::vector<uint32_t>& iv,
const std::vector<uint32_t>& selector) {
return RowMap(row_map_algorithms::SelectIvWithIv(iv, selector));
}
// O(N), but 64 times faster than doing it bit by bit, as we compare words in
// BitVectors.
Variant IntersectInternal(BitVector& first, const BitVector& second) {
first.And(second);
return std::move(first);
}
// O(1) complexity.
Variant IntersectInternal(Range first, Range second) {
// If both RowMaps have ranges, we can just take the smallest intersection
// of them as the new RowMap.
// We have this as an explicit fast path as this is very common for
// constraints on id and sorted columns to satisfy this condition.
OutputIndex start = std::max(first.start, second.start);
OutputIndex end = std::max(start, std::min(first.end, second.end));
return Range{start, end};
}
// O(N + k) complexity, where N is the size of |second| and k is the number of
// elements that have to be removed from |first|.
Variant IntersectInternal(std::vector<OutputIndex>& first,
const std::vector<OutputIndex>& second) {
std::unordered_set<OutputIndex> lookup(second.begin(), second.end());
first.erase(std::remove_if(first.begin(), first.end(),
[lookup](OutputIndex ind) {
return lookup.find(ind) == lookup.end();
}),
first.end());
return std::move(first);
}
// O(1) complexity.
Variant IntersectInternal(Range range, const BitVector& bv) {
return bv.IntersectRange(range.start, range.end);
}
Variant IntersectInternal(BitVector& bv, Range range) {
return IntersectInternal(range, bv);
}
Variant IntersectInternal(const std::vector<OutputIndex>& index_vec,
const BitVector& bv) {
std::vector<OutputIndex> new_vec(index_vec.begin(), index_vec.end());
new_vec.erase(std::remove_if(new_vec.begin(), new_vec.end(),
[&bv](uint32_t i) { return !bv.IsSet(i); }),
new_vec.end());
return std::move(new_vec);
}
Variant IntersectInternal(const BitVector& bv,
const std::vector<OutputIndex>& index_vec) {
return IntersectInternal(index_vec, bv);
}
Variant IntersectInternal(Range range,
const std::vector<OutputIndex>& index_vec) {
std::vector<OutputIndex> new_vec(index_vec.begin(), index_vec.end());
new_vec.erase(std::remove_if(new_vec.begin(), new_vec.end(),
[range](uint32_t i) {
return i < range.start || i >= range.end;
}),
new_vec.end());
return std::move(new_vec);
}
Variant IntersectInternal(const std::vector<OutputIndex>& index_vec,
Range range) {
return IntersectInternal(range, index_vec);
}
} // namespace
RowMap::RowMap() : RowMap(Range()) {}
RowMap::RowMap(uint32_t start, uint32_t end) : data_(Range{start, end}) {}
RowMap::RowMap(Variant def) : data_(std::move(def)) {}
RowMap::RowMap(Range r) : data_(r) {}
// Creates a RowMap backed by a BitVector.
RowMap::RowMap(BitVector bit_vector) : data_(std::move(bit_vector)) {}
// Creates a RowMap backed by an std::vector<uint32_t>.
RowMap::RowMap(IndexVector vec) : data_(vec) {}
RowMap RowMap::Copy() const {
if (const auto* range = std::get_if<Range>(&data_)) {
return RowMap(*range);
}
if (const auto* bv = std::get_if<BitVector>(&data_)) {
return RowMap(bv->Copy());
}
if (const auto* vec = std::get_if<IndexVector>(&data_)) {
return RowMap(*vec);
}
NoVariantMatched();
}
OutputIndex RowMap::Max() const {
if (const auto* range = std::get_if<Range>(&data_)) {
return range->end;
}
if (const auto* bv = std::get_if<BitVector>(&data_)) {
return bv->size();
}
if (const auto* vec = std::get_if<IndexVector>(&data_)) {
return vec->empty() ? 0 : *std::max_element(vec->begin(), vec->end()) + 1;
}
NoVariantMatched();
}
RowMap RowMap::SelectRowsSlow(const RowMap& selector) const {
return std::visit(
[](const auto& def, const auto& selector_def) {
return Select(def, selector_def);
},
data_, selector.data_);
}
void RowMap::Intersect(const RowMap& second) {
data_ = std::visit(
[](auto& def, auto& selector_def) {
return IntersectInternal(def, selector_def);
},
data_, second.data_);
}
RowMap::Iterator::Iterator(const RowMap* rm) : rm_(rm) {
if (const auto* range = std::get_if<Range>(&rm_->data_)) {
ordinal_ = range->start;
return;
}
if (const auto* bv = std::get_if<BitVector>(&rm_->data_)) {
results_ = bv->GetSetBitIndices();
return;
}
}
} // namespace trace_processor
} // namespace perfetto