1
2 /*
3 * Copyright (C) 2023 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <variant>
19
20 #include "perfetto/ext/base/status_or.h"
21 #include "src/trace_processor/db/column.h"
22 #include "src/trace_processor/db/numeric_storage.h"
23
24 namespace perfetto {
25 namespace trace_processor {
26 namespace column {
27
28 namespace {
29
30 // Templated part of FastPathComparison.
31 template <typename T>
TypedFastPathComparison(std::optional<NumericValue> val,FilterOp op,const T * start,uint32_t num_elements,BitVector::Builder & builder)32 inline void TypedFastPathComparison(std::optional<NumericValue> val,
33 FilterOp op,
34 const T* start,
35 uint32_t num_elements,
36 BitVector::Builder& builder) {
37 if (!val) {
38 builder.Skip(num_elements);
39 return;
40 }
41 std::visit(
42 [val, start, num_elements, &builder](auto comparator) {
43 T typed_val = std::get<T>(*val);
44 for (uint32_t i = 0; i < num_elements; i += BitVector::kBitsInWord) {
45 uint64_t word = 0;
46 // This part should be optimised by SIMD and is expected to be fast.
47 for (uint32_t k = 0; k < BitVector::kBitsInWord; ++k) {
48 bool comp_result = comparator(start[i + k], typed_val);
49 word |= static_cast<uint64_t>(comp_result) << k;
50 }
51 builder.AppendWord(word);
52 }
53 },
54 GetFilterOpVariant<T>(op));
55 }
56
57 // Templated part of SlowPathComparison.
58 template <typename T>
TypedSlowPathComparison(std::optional<NumericValue> val,FilterOp op,const T * start,uint32_t num_elements,BitVector::Builder & builder)59 inline void TypedSlowPathComparison(std::optional<NumericValue> val,
60 FilterOp op,
61 const T* start,
62 uint32_t num_elements,
63 BitVector::Builder& builder) {
64 if (!val) {
65 builder.Skip(num_elements);
66 return;
67 }
68 std::visit(
69 [val, start, num_elements, &builder](auto comparator) {
70 T typed_val = std::get<T>(*val);
71 for (uint32_t i = 0; i < num_elements; ++i) {
72 builder.Append(comparator(start[i], typed_val));
73 }
74 },
75 GetFilterOpVariant<T>(op));
76 }
77
78 } // namespace
79
StableSort(uint32_t * rows,uint32_t rows_size) const80 void NumericStorage::StableSort(uint32_t* rows, uint32_t rows_size) const {
81 NumericValue val = *GetNumericTypeVariant(type_, SqlValue::Long(0));
82 std::visit(
83 [this, &rows, rows_size](auto val_data) {
84 using T = decltype(val_data);
85 const T* typed_start = static_cast<const T*>(data_);
86 std::stable_sort(rows, rows + rows_size,
87 [typed_start](uint32_t a_idx, uint32_t b_idx) {
88 T first_val = typed_start[a_idx];
89 T second_val = typed_start[b_idx];
90 return first_val < second_val;
91 });
92 },
93 val);
94 }
95
96 // Responsible for invoking templated version of FastPathComparison.
CompareFast(FilterOp op,SqlValue sql_val,uint32_t offset,uint32_t num_elements,BitVector::Builder & builder) const97 void NumericStorage::CompareFast(FilterOp op,
98 SqlValue sql_val,
99 uint32_t offset,
100 uint32_t num_elements,
101 BitVector::Builder& builder) const {
102 PERFETTO_DCHECK(num_elements % BitVector::kBitsInWord == 0);
103 std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
104
105 // If the value is invalid we should just ignore those elements.
106 if (!val.has_value() || op == FilterOp::kIsNotNull ||
107 op == FilterOp::kIsNull || op == FilterOp::kGlob) {
108 builder.Skip(num_elements);
109 return;
110 }
111 std::visit(
112 [this, op, offset, num_elements, &builder](auto num_val) {
113 using T = decltype(num_val);
114 auto* typed_start = static_cast<const T*>(data_) + offset;
115 TypedFastPathComparison(num_val, op, typed_start, num_elements,
116 builder);
117 },
118 *val);
119 }
120
121 // Responsible for invoking templated version of SlowPathComparison.
CompareSlow(FilterOp op,SqlValue sql_val,uint32_t offset,uint32_t num_elements,BitVector::Builder & builder) const122 void NumericStorage::CompareSlow(FilterOp op,
123 SqlValue sql_val,
124 uint32_t offset,
125 uint32_t num_elements,
126 BitVector::Builder& builder) const {
127 std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
128
129 // If the value is invalid we should just ignore those elements.
130 if (!val.has_value() || op == FilterOp::kIsNotNull ||
131 op == FilterOp::kIsNull || op == FilterOp::kGlob) {
132 builder.Skip(num_elements);
133 return;
134 }
135
136 std::visit(
137 [this, op, offset, num_elements, &builder](auto val) {
138 using T = decltype(val);
139 auto* typed_start = static_cast<const T*>(data_) + offset;
140 TypedSlowPathComparison(val, op, typed_start, num_elements, builder);
141 },
142 *val);
143 }
144
UpperBoundIndex(NumericValue val) const145 uint32_t NumericStorage::UpperBoundIndex(NumericValue val) const {
146 return std::visit(
147 [this](auto val_data) {
148 using T = decltype(val_data);
149 const T* typed_start = static_cast<const T*>(data_);
150 auto upper =
151 std::upper_bound(typed_start, typed_start + size_, val_data);
152 return static_cast<uint32_t>(std::distance(typed_start, upper));
153 },
154 val);
155 }
156
157 // As we don't template those functions, we need to use std::visitor to type
158 // `start`, hence this wrapping.
LowerBoundIndex(NumericValue val) const159 uint32_t NumericStorage::LowerBoundIndex(NumericValue val) const {
160 return std::visit(
161 [this](auto val_data) {
162 using T = decltype(val_data);
163 const T* typed_start = static_cast<const T*>(data_);
164 auto lower =
165 std::lower_bound(typed_start, typed_start + size_, val_data);
166 return static_cast<uint32_t>(std::distance(typed_start, lower));
167 },
168 val);
169 }
170
CompareSorted(FilterOp op,SqlValue sql_val,RowMap & rm) const171 void NumericStorage::CompareSorted(FilterOp op,
172 SqlValue sql_val,
173 RowMap& rm) const {
174 std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
175 if (!val.has_value() || op == FilterOp::kIsNotNull ||
176 op == FilterOp::kIsNull || op == FilterOp::kGlob) {
177 rm.Clear();
178 return;
179 }
180
181 switch (op) {
182 case FilterOp::kEq: {
183 uint32_t beg = LowerBoundIndex(*val);
184 uint32_t end = UpperBoundIndex(*val);
185 RowMap sec(beg, end);
186 rm.Intersect(sec);
187 return;
188 }
189 case FilterOp::kLe: {
190 uint32_t end = UpperBoundIndex(*val);
191 RowMap sec(0, end);
192 rm.Intersect(sec);
193 return;
194 }
195 case FilterOp::kLt: {
196 uint32_t end = LowerBoundIndex(*val);
197 RowMap sec(0, end);
198 rm.Intersect(sec);
199 return;
200 }
201 case FilterOp::kGe: {
202 uint32_t beg = LowerBoundIndex(*val);
203 RowMap sec(beg, size_);
204 rm.Intersect(sec);
205 return;
206 }
207 case FilterOp::kGt: {
208 uint32_t beg = UpperBoundIndex(*val);
209 RowMap sec(beg, size_);
210 rm.Intersect(sec);
211 return;
212 }
213 case FilterOp::kNe:
214 case FilterOp::kIsNull:
215 case FilterOp::kIsNotNull:
216 case FilterOp::kGlob:
217 rm.Clear();
218 }
219 return;
220 }
221
UpperBoundIndex(NumericValue val,uint32_t * order) const222 uint32_t NumericStorage::UpperBoundIndex(NumericValue val,
223 uint32_t* order) const {
224 return std::visit(
225 [this, order](auto val_data) {
226 using T = decltype(val_data);
227 const T* typed_start = static_cast<const T*>(data_);
228 auto upper = std::upper_bound(order, order + size_, val_data,
229 [typed_start](T val, uint32_t index) {
230 return val < *(typed_start + index);
231 });
232 return static_cast<uint32_t>(std::distance(order, upper));
233 },
234 val);
235 }
236
237 // As we don't template those functions, we need to use std::visitor to type
238 // `start`, hence this wrapping.
LowerBoundIndex(NumericValue val,uint32_t * order) const239 uint32_t NumericStorage::LowerBoundIndex(NumericValue val,
240 uint32_t* order) const {
241 return std::visit(
242 [this, order](auto val_data) {
243 using T = decltype(val_data);
244 const T* typed_start = static_cast<const T*>(data_);
245 auto lower = std::lower_bound(order, order + size_, val_data,
246 [typed_start](uint32_t index, T val) {
247 return *(typed_start + index) < val;
248 });
249 return static_cast<uint32_t>(std::distance(order, lower));
250 },
251 val);
252 }
253
CompareSortedIndexes(FilterOp op,SqlValue sql_val,uint32_t * order,RowMap & rm) const254 void NumericStorage::CompareSortedIndexes(FilterOp op,
255 SqlValue sql_val,
256 uint32_t* order,
257 RowMap& rm) const {
258 std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
259 if (!val.has_value() || op == FilterOp::kIsNotNull ||
260 op == FilterOp::kIsNull || op == FilterOp::kGlob) {
261 rm.Clear();
262 return;
263 }
264
265 switch (op) {
266 case FilterOp::kEq: {
267 uint32_t beg = LowerBoundIndex(*val, order);
268 uint32_t end = UpperBoundIndex(*val, order);
269 std::vector<uint32_t> index(order + beg, order + end);
270 rm.Intersect(RowMap(std::move(index)));
271 return;
272 }
273 case FilterOp::kLe: {
274 uint32_t end = UpperBoundIndex(*val, order);
275 std::vector<uint32_t> index(order, order + end);
276 rm.Intersect(RowMap(std::move(index)));
277 return;
278 }
279 case FilterOp::kLt: {
280 uint32_t end = LowerBoundIndex(*val, order);
281 std::vector<uint32_t> index(order, order + end);
282 rm.Intersect(RowMap(std::move(index)));
283 return;
284 }
285 case FilterOp::kGe: {
286 uint32_t beg = LowerBoundIndex(*val, order);
287 std::vector<uint32_t> index(order + beg, order + size_);
288 rm.Intersect(RowMap(std::move(index)));
289 return;
290 }
291 case FilterOp::kGt: {
292 uint32_t beg = UpperBoundIndex(*val, order);
293 std::vector<uint32_t> index(order + beg, order + size_);
294 rm.Intersect(RowMap(std::move(index)));
295 return;
296 }
297 case FilterOp::kNe:
298 case FilterOp::kIsNull:
299 case FilterOp::kIsNotNull:
300 case FilterOp::kGlob:
301 rm.Clear();
302 }
303 return;
304 }
305
306 } // namespace column
307 } // namespace trace_processor
308 } // namespace perfetto
309