• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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