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
IntersectInternal(BitVector & first,const BitVector & second)156 Variant IntersectInternal(BitVector& first, const BitVector& second) {
157 first.And(second);
158 return std::move(first);
159 }
160
IntersectInternal(Range first,Range second)161 Variant IntersectInternal(Range first, Range second) {
162 // If both RowMaps have ranges, we can just take the smallest intersection
163 // of them as the new RowMap.
164 // We have this as an explicit fast path as this is very common for
165 // constraints on id and sorted columns to satisfy this condition.
166 OutputIndex start = std::max(first.start, second.start);
167 OutputIndex end = std::max(start, std::min(first.end, second.end));
168 return Range{start, end};
169 }
170
IntersectInternal(std::vector<OutputIndex> & first,const std::vector<OutputIndex> & second)171 Variant IntersectInternal(std::vector<OutputIndex>& first,
172 const std::vector<OutputIndex>& second) {
173 std::unordered_set<OutputIndex> lookup(second.begin(), second.end());
174 first.erase(std::remove_if(first.begin(), first.end(),
175 [lookup](OutputIndex ind) {
176 return lookup.find(ind) == lookup.end();
177 }),
178 first.end());
179 return std::move(first);
180 }
181
IntersectInternal(Range range,const BitVector & bv)182 Variant IntersectInternal(Range range, const BitVector& bv) {
183 return bv.IntersectRange(range.start, range.end);
184 }
185
IntersectInternal(BitVector & bv,Range range)186 Variant IntersectInternal(BitVector& bv, Range range) {
187 return IntersectInternal(range, bv);
188 }
189
IntersectInternal(const std::vector<OutputIndex> & index_vec,const BitVector & bv)190 Variant IntersectInternal(const std::vector<OutputIndex>& index_vec,
191 const BitVector& bv) {
192 std::vector<OutputIndex> new_vec(index_vec.begin(), index_vec.end());
193 new_vec.erase(std::remove_if(new_vec.begin(), new_vec.end(),
194 [&bv](uint32_t i) { return !bv.IsSet(i); }),
195 new_vec.end());
196 return std::move(new_vec);
197 }
198
IntersectInternal(const BitVector & bv,const std::vector<OutputIndex> & index_vec)199 Variant IntersectInternal(const BitVector& bv,
200 const std::vector<OutputIndex>& index_vec) {
201 return IntersectInternal(index_vec, bv);
202 }
203
IntersectInternal(Range range,const std::vector<OutputIndex> & index_vec)204 Variant IntersectInternal(Range range,
205 const std::vector<OutputIndex>& index_vec) {
206 std::vector<OutputIndex> new_vec(index_vec.begin(), index_vec.end());
207 new_vec.erase(std::remove_if(new_vec.begin(), new_vec.end(),
208 [range](uint32_t i) {
209 return i < range.start || i >= range.end;
210 }),
211 new_vec.end());
212 return std::move(new_vec);
213 }
214
IntersectInternal(const std::vector<OutputIndex> & index_vec,Range range)215 Variant IntersectInternal(const std::vector<OutputIndex>& index_vec,
216 Range range) {
217 return IntersectInternal(range, index_vec);
218 }
219
220 } // namespace
221
RowMap()222 RowMap::RowMap() : RowMap(Range()) {}
223
RowMap(uint32_t start,uint32_t end)224 RowMap::RowMap(uint32_t start, uint32_t end) : data_(Range{start, end}) {}
225
RowMap(Variant def)226 RowMap::RowMap(Variant def) : data_(std::move(def)) {}
227
RowMap(Range r)228 RowMap::RowMap(Range r) : data_(r) {}
229
230 // Creates a RowMap backed by a BitVector.
RowMap(BitVector bit_vector)231 RowMap::RowMap(BitVector bit_vector) : data_(std::move(bit_vector)) {}
232
233 // Creates a RowMap backed by an std::vector<uint32_t>.
RowMap(IndexVector vec)234 RowMap::RowMap(IndexVector vec) : data_(vec) {}
235
Copy() const236 RowMap RowMap::Copy() const {
237 if (const auto* range = std::get_if<Range>(&data_)) {
238 return RowMap(*range);
239 }
240 if (const auto* bv = std::get_if<BitVector>(&data_)) {
241 return RowMap(bv->Copy());
242 }
243 if (const auto* vec = std::get_if<IndexVector>(&data_)) {
244 return RowMap(*vec);
245 }
246 NoVariantMatched();
247 }
248
Max() const249 OutputIndex RowMap::Max() const {
250 if (const auto* range = std::get_if<Range>(&data_)) {
251 return range->end;
252 }
253 if (const auto* bv = std::get_if<BitVector>(&data_)) {
254 return bv->size();
255 }
256 if (const auto* vec = std::get_if<IndexVector>(&data_)) {
257 return vec->empty() ? 0 : *std::max_element(vec->begin(), vec->end()) + 1;
258 }
259 NoVariantMatched();
260 }
261
SelectRowsSlow(const RowMap & selector) const262 RowMap RowMap::SelectRowsSlow(const RowMap& selector) const {
263 return std::visit(
264 [](const auto& def, const auto& selector_def) {
265 return Select(def, selector_def);
266 },
267 data_, selector.data_);
268 }
269
Intersect(const RowMap & second)270 void RowMap::Intersect(const RowMap& second) {
271 data_ = std::visit(
272 [](auto& def, auto& selector_def) {
273 return IntersectInternal(def, selector_def);
274 },
275 data_, second.data_);
276 }
277
Iterator(const RowMap * rm)278 RowMap::Iterator::Iterator(const RowMap* rm) : rm_(rm) {
279 if (const auto* range = std::get_if<Range>(&rm_->data_)) {
280 ordinal_ = range->start;
281 return;
282 }
283 if (const auto* bv = std::get_if<BitVector>(&rm_->data_)) {
284 results_ = bv->GetSetBitIndices();
285 return;
286 }
287 }
288
289 } // namespace trace_processor
290 } // namespace perfetto
291