1 /*
2 * Copyright (C) 2019 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 #include "src/trace_processor/containers/row_map.h"
18
19 #include <algorithm>
20 #include <cstdint>
21 #include <unordered_set>
22 #include <utility>
23 #include <variant>
24 #include <vector>
25
26 #include "perfetto/base/logging.h"
27 #include "src/trace_processor/containers/bit_vector.h"
28 #include "src/trace_processor/containers/row_map_algorithms.h"
29
30 namespace perfetto {
31 namespace trace_processor {
32
33 namespace {
34
35 using Range = RowMap::Range;
36 using OutputIndex = RowMap::OutputIndex;
37 using Variant = std::variant<Range, BitVector, std::vector<OutputIndex>>;
38
Select(Range range,Range selector)39 RowMap Select(Range range, Range selector) {
40 PERFETTO_DCHECK(selector.start <= selector.end);
41 PERFETTO_DCHECK(selector.end <= range.size());
42
43 return RowMap(range.start + selector.start, range.start + selector.end);
44 }
45
Select(Range range,const BitVector & selector)46 RowMap Select(Range range, const BitVector& selector) {
47 PERFETTO_DCHECK(selector.size() <= range.size());
48
49 // If |start| == 0 and |selector.size()| <= |end - start| (which is a
50 // precondition for this function), the BitVector we generate is going to be
51 // exactly |selector|.
52 //
53 // This is a fast path for the common situation where, post-filtering,
54 // SelectRows is called on all the table RowMaps with a BitVector. The self
55 // RowMap will always be a range so we expect this case to be hit at least
56 // once every filter operation.
57 if (range.start == 0u)
58 return RowMap(selector.Copy());
59
60 // We only need to resize to |start| + |selector.size()| as we know any rows
61 // not covered by |selector| are going to be removed below.
62 BitVector bv(range.start, false);
63 bv.Resize(range.start + selector.size(), true);
64
65 bv.UpdateSetBits(selector);
66 return RowMap(std::move(bv));
67 }
68
Select(Range range,const std::vector<OutputIndex> & selector)69 RowMap Select(Range range, const std::vector<OutputIndex>& selector) {
70 std::vector<uint32_t> iv(selector.size());
71 for (uint32_t i = 0; i < selector.size(); ++i) {
72 PERFETTO_DCHECK(selector[i] < range.size());
73 iv[i] = selector[i] + range.start;
74 }
75 return RowMap(std::move(iv));
76 }
77
Select(const BitVector & bv,Range selector)78 RowMap Select(const BitVector& bv, Range selector) {
79 PERFETTO_DCHECK(selector.end <= bv.CountSetBits());
80 if (selector.empty()) {
81 return {};
82 }
83 // If we're simply selecting every element in the bitvector, just
84 // return a copy of the BitVector without iterating.
85 if (selector.start == 0 && selector.end == bv.CountSetBits()) {
86 return RowMap(bv.Copy());
87 }
88 return RowMap(bv.IntersectRange(bv.IndexOfNthSet(selector.start),
89 bv.IndexOfNthSet(selector.end - 1) + 1));
90 }
91
Select(const BitVector & bv,const BitVector & selector)92 RowMap Select(const BitVector& bv, const BitVector& selector) {
93 BitVector ret = bv.Copy();
94 ret.UpdateSetBits(selector);
95 return RowMap(std::move(ret));
96 }
97
Select(const BitVector & bv,const std::vector<uint32_t> & selector)98 RowMap Select(const BitVector& bv, const std::vector<uint32_t>& selector) {
99 // The value of this constant was found by considering the benchmarks
100 // |BM_SelectBvWithIvByConvertToIv| and |BM_SelectBvWithIvByIndexOfNthSet|.
101 //
102 // We use this to find the ratio between |bv.CountSetBits()| and
103 // |selector.size()| where |SelectBvWithIvByIndexOfNthSet| was found to be
104 // faster than |SelectBvWithIvByConvertToIv|.
105 //
106 // Note: as of writing this, the benchmarks do not take into account the fill
107 // ratio of the BitVector; they assume 50% rate which almost never happens in
108 // practice. In the future, we could also take this into account (by
109 // considering the ratio between bv.size() and bv.CountSetBits()) but this
110 // causes an explosion in the state space for the benchmark so we're not
111 // considering this today.
112 //
113 // The current value of the constant was picked by running these benchmarks on
114 // a E5-2690 v4 and finding the crossover point using a spreadsheet.
115 constexpr uint32_t kIndexOfSetBitToSelectorRatio = 4;
116
117 // If the selector is larger than a threshold, it's more efficient to convert
118 // the entire BitVector to an index vector and use SelectIvWithIv instead.
119 if (bv.CountSetBits() / kIndexOfSetBitToSelectorRatio < selector.size()) {
120 return RowMap(
121 row_map_algorithms::SelectBvWithIvByConvertToIv(bv, selector));
122 }
123 return RowMap(
124 row_map_algorithms::SelectBvWithIvByIndexOfNthSet(bv, selector));
125 }
126
Select(const std::vector<uint32_t> & iv,Range selector)127 RowMap Select(const std::vector<uint32_t>& iv, Range selector) {
128 PERFETTO_DCHECK(selector.end <= iv.size());
129
130 std::vector<uint32_t> ret(selector.size());
131 for (uint32_t i = selector.start; i < selector.end; ++i) {
132 ret[i - selector.start] = iv[i];
133 }
134 return RowMap(std::move(ret));
135 }
136
Select(const std::vector<uint32_t> & iv,const BitVector & selector)137 RowMap Select(const std::vector<uint32_t>& iv, const BitVector& selector) {
138 PERFETTO_DCHECK(selector.size() <= iv.size());
139
140 std::vector<uint32_t> copy = iv;
141 copy.resize(selector.size());
142
143 uint32_t idx = 0;
144 auto it = std::remove_if(
145 copy.begin(), copy.end(),
146 [&idx, &selector](uint32_t) { return !selector.IsSet(idx++); });
147 copy.erase(it, copy.end());
148 return RowMap(std::move(copy));
149 }
150
Select(const std::vector<uint32_t> & iv,const std::vector<uint32_t> & selector)151 RowMap Select(const std::vector<uint32_t>& iv,
152 const std::vector<uint32_t>& selector) {
153 return RowMap(row_map_algorithms::SelectIvWithIv(iv, selector));
154 }
155
156 // O(N), but 64 times faster than doing it bit by bit, as we compare words in
157 // BitVectors.
IntersectInternal(BitVector & first,const BitVector & second)158 Variant IntersectInternal(BitVector& first, const BitVector& second) {
159 first.And(second);
160 return std::move(first);
161 }
162
163 // O(1) complexity.
IntersectInternal(Range first,Range second)164 Variant IntersectInternal(Range first, Range second) {
165 // If both RowMaps have ranges, we can just take the smallest intersection
166 // of them as the new RowMap.
167 // We have this as an explicit fast path as this is very common for
168 // constraints on id and sorted columns to satisfy this condition.
169 OutputIndex start = std::max(first.start, second.start);
170 OutputIndex end = std::max(start, std::min(first.end, second.end));
171 return Range{start, end};
172 }
173
174 // O(N + k) complexity, where N is the size of |second| and k is the number of
175 // elements that have to be removed from |first|.
IntersectInternal(std::vector<OutputIndex> & first,const std::vector<OutputIndex> & second)176 Variant IntersectInternal(std::vector<OutputIndex>& first,
177 const std::vector<OutputIndex>& second) {
178 std::unordered_set<OutputIndex> lookup(second.begin(), second.end());
179 first.erase(std::remove_if(first.begin(), first.end(),
180 [lookup](OutputIndex ind) {
181 return lookup.find(ind) == lookup.end();
182 }),
183 first.end());
184 return std::move(first);
185 }
186
187 // O(1) complexity.
IntersectInternal(Range range,const BitVector & bv)188 Variant IntersectInternal(Range range, const BitVector& bv) {
189 return bv.IntersectRange(range.start, range.end);
190 }
191
IntersectInternal(BitVector & bv,Range range)192 Variant IntersectInternal(BitVector& bv, Range range) {
193 return IntersectInternal(range, bv);
194 }
195
IntersectInternal(const std::vector<OutputIndex> & index_vec,const BitVector & bv)196 Variant IntersectInternal(const std::vector<OutputIndex>& index_vec,
197 const BitVector& bv) {
198 std::vector<OutputIndex> new_vec(index_vec.begin(), index_vec.end());
199 new_vec.erase(std::remove_if(new_vec.begin(), new_vec.end(),
200 [&bv](uint32_t i) { return !bv.IsSet(i); }),
201 new_vec.end());
202 return std::move(new_vec);
203 }
204
IntersectInternal(const BitVector & bv,const std::vector<OutputIndex> & index_vec)205 Variant IntersectInternal(const BitVector& bv,
206 const std::vector<OutputIndex>& index_vec) {
207 return IntersectInternal(index_vec, bv);
208 }
209
IntersectInternal(Range range,const std::vector<OutputIndex> & index_vec)210 Variant IntersectInternal(Range range,
211 const std::vector<OutputIndex>& index_vec) {
212 std::vector<OutputIndex> new_vec(index_vec.begin(), index_vec.end());
213 new_vec.erase(std::remove_if(new_vec.begin(), new_vec.end(),
214 [range](uint32_t i) {
215 return i < range.start || i >= range.end;
216 }),
217 new_vec.end());
218 return std::move(new_vec);
219 }
220
IntersectInternal(const std::vector<OutputIndex> & index_vec,Range range)221 Variant IntersectInternal(const std::vector<OutputIndex>& index_vec,
222 Range range) {
223 return IntersectInternal(range, index_vec);
224 }
225
226 } // namespace
227
RowMap()228 RowMap::RowMap() : RowMap(Range()) {}
229
RowMap(uint32_t start,uint32_t end)230 RowMap::RowMap(uint32_t start, uint32_t end) : data_(Range{start, end}) {}
231
RowMap(Variant def)232 RowMap::RowMap(Variant def) : data_(std::move(def)) {}
233
RowMap(Range r)234 RowMap::RowMap(Range r) : data_(r) {}
235
236 // Creates a RowMap backed by a BitVector.
RowMap(BitVector bit_vector)237 RowMap::RowMap(BitVector bit_vector) : data_(std::move(bit_vector)) {}
238
239 // Creates a RowMap backed by an std::vector<uint32_t>.
RowMap(IndexVector vec)240 RowMap::RowMap(IndexVector vec) : data_(vec) {}
241
Copy() const242 RowMap RowMap::Copy() const {
243 if (const auto* range = std::get_if<Range>(&data_)) {
244 return RowMap(*range);
245 }
246 if (const auto* bv = std::get_if<BitVector>(&data_)) {
247 return RowMap(bv->Copy());
248 }
249 if (const auto* vec = std::get_if<IndexVector>(&data_)) {
250 return RowMap(*vec);
251 }
252 NoVariantMatched();
253 }
254
Max() const255 OutputIndex RowMap::Max() const {
256 if (const auto* range = std::get_if<Range>(&data_)) {
257 return range->end;
258 }
259 if (const auto* bv = std::get_if<BitVector>(&data_)) {
260 return bv->size();
261 }
262 if (const auto* vec = std::get_if<IndexVector>(&data_)) {
263 return vec->empty() ? 0 : *std::max_element(vec->begin(), vec->end()) + 1;
264 }
265 NoVariantMatched();
266 }
267
SelectRowsSlow(const RowMap & selector) const268 RowMap RowMap::SelectRowsSlow(const RowMap& selector) const {
269 return std::visit(
270 [](const auto& def, const auto& selector_def) {
271 return Select(def, selector_def);
272 },
273 data_, selector.data_);
274 }
275
Intersect(const RowMap & second)276 void RowMap::Intersect(const RowMap& second) {
277 data_ = std::visit(
278 [](auto& def, auto& selector_def) {
279 return IntersectInternal(def, selector_def);
280 },
281 data_, second.data_);
282 }
283
Iterator(const RowMap * rm)284 RowMap::Iterator::Iterator(const RowMap* rm) : rm_(rm) {
285 if (const auto* range = std::get_if<Range>(&rm_->data_)) {
286 ordinal_ = range->start;
287 return;
288 }
289 if (const auto* bv = std::get_if<BitVector>(&rm_->data_)) {
290 results_ = bv->GetSetBitIndices();
291 return;
292 }
293 }
294
295 } // namespace trace_processor
296 } // namespace perfetto
297