• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 <algorithm>
18 #include <cstdint>
19 #include <utility>
20 #include <vector>
21 
22 #include <sys/types.h>
23 #include "perfetto/base/logging.h"
24 #include "perfetto/trace_processor/basic_types.h"
25 #include "src/trace_processor/containers/row_map.h"
26 #include "src/trace_processor/db/column/data_layer.h"
27 #include "src/trace_processor/db/column/types.h"
28 #include "src/trace_processor/db/query_executor.h"
29 #include "src/trace_processor/db/table.h"
30 
31 namespace perfetto::trace_processor {
32 
33 namespace {
34 
35 using Range = RowMap::Range;
36 
37 }  // namespace
38 
FilterColumn(const Constraint & c,const column::DataLayerChain & chain,RowMap * rm)39 void QueryExecutor::FilterColumn(const Constraint& c,
40                                  const column::DataLayerChain& chain,
41                                  RowMap* rm) {
42   // Shortcut of empty row map.
43   uint32_t rm_size = rm->size();
44   if (rm_size == 0)
45     return;
46 
47   uint32_t rm_first = rm->Get(0);
48   if (rm_size == 1) {
49     switch (chain.SingleSearch(c.op, c.value, rm_first)) {
50       case SingleSearchResult::kMatch:
51         return;
52       case SingleSearchResult::kNoMatch:
53         rm->Clear();
54         return;
55       case SingleSearchResult::kNeedsFullSearch:
56         break;
57     }
58   }
59 
60   // Comparison of NULL with any operation apart from |IS_NULL| and
61   // |IS_NOT_NULL| should return no rows.
62   if (c.value.is_null() && c.op != FilterOp::kIsNull &&
63       c.op != FilterOp::kIsNotNull) {
64     rm->Clear();
65     return;
66   }
67 
68   uint32_t rm_last = rm->Get(rm_size - 1);
69   uint32_t range_size = rm_last - rm_first;
70 
71   // If the number of elements in the rowmap is small or the number of
72   // elements is less than 1/10th of the range, use indexed filtering.
73   // TODO(b/283763282): use Overlay estimations.
74   bool disallows_index_search = rm->IsRange();
75   bool prefers_index_search =
76       rm->IsIndexVector() || rm_size < 1024 || rm_size * 10 < range_size;
77 
78   if (!disallows_index_search && prefers_index_search) {
79     IndexSearch(c, chain, rm);
80     return;
81   }
82   LinearSearch(c, chain, rm);
83 }
84 
LinearSearch(const Constraint & c,const column::DataLayerChain & chain,RowMap * rm)85 void QueryExecutor::LinearSearch(const Constraint& c,
86                                  const column::DataLayerChain& chain,
87                                  RowMap* rm) {
88   // TODO(b/283763282): Align these to word boundaries.
89   Range bounds(rm->Get(0), rm->Get(rm->size() - 1) + 1);
90 
91   // Search the storage.
92   RangeOrBitVector res = chain.Search(c.op, c.value, bounds);
93   if (rm->IsRange()) {
94     if (res.IsRange()) {
95       Range range = std::move(res).TakeIfRange();
96       *rm = RowMap(range.start, range.end);
97     } else {
98       // The BitVector was already limited on the RowMap when created, so we
99       // can take it as it is.
100       *rm = RowMap(std::move(res).TakeIfBitVector());
101     }
102     return;
103   }
104 
105   if (res.IsRange()) {
106     Range range = std::move(res).TakeIfRange();
107     rm->Intersect(RowMap(range.start, range.end));
108     return;
109   }
110   rm->Intersect(RowMap(std::move(res).TakeIfBitVector()));
111 }
112 
IndexSearch(const Constraint & c,const column::DataLayerChain & chain,RowMap * rm)113 void QueryExecutor::IndexSearch(const Constraint& c,
114                                 const column::DataLayerChain& chain,
115                                 RowMap* rm) {
116   // Create outmost TableIndexVector.
117   std::vector<uint32_t> table_indices = std::move(*rm).TakeAsIndexVector();
118 
119   using Indices = column::DataLayerChain::Indices;
120   Indices indices = Indices::Create(table_indices, Indices::State::kMonotonic);
121   chain.IndexSearch(c.op, c.value, indices);
122 
123   PERFETTO_DCHECK(indices.tokens.size() <= table_indices.size());
124   for (uint32_t i = 0; i < indices.tokens.size(); ++i) {
125     table_indices[i] = indices.tokens[i].payload;
126   }
127   table_indices.resize(indices.tokens.size());
128   PERFETTO_DCHECK(std::is_sorted(table_indices.begin(), table_indices.end()));
129   *rm = RowMap(std::move(table_indices));
130 }
131 
FilterLegacy(const Table * table,const std::vector<Constraint> & c_vec)132 RowMap QueryExecutor::FilterLegacy(const Table* table,
133                                    const std::vector<Constraint>& c_vec) {
134   RowMap rm(0, table->row_count());
135   for (const auto& c : c_vec) {
136     FilterColumn(c, table->ChainForColumn(c.col_idx), &rm);
137   }
138   return rm;
139 }
140 
SortLegacy(const Table * table,const std::vector<Order> & ob,std::vector<uint32_t> & out)141 void QueryExecutor::SortLegacy(const Table* table,
142                                const std::vector<Order>& ob,
143                                std::vector<uint32_t>& out) {
144   // Setup the sort token payload to match the input vector of indices. The
145   // value of the payload will be untouched by the algorithm even while the
146   // order changes to match the ordering defined by the input constraint set.
147   std::vector<column::DataLayerChain::SortToken> rows(out.size());
148   for (uint32_t i = 0; i < out.size(); ++i) {
149     rows[i].payload = out[i];
150   }
151 
152   // As our data is columnar, it's always more efficient to sort one column
153   // at a time rather than try and sort lexiographically all at once.
154   // To preserve correctness, we need to stably sort the index vector once
155   // for each order by in *reverse* order. Reverse order is important as it
156   // preserves the lexiographical property.
157   //
158   // For example, suppose we have the following:
159   // Table {
160   //   Column x;
161   //   Column y
162   //   Column z;
163   // }
164   //
165   // Then, to sort "y asc, x desc", we could do one of two things:
166   //  1) sort the index vector all at once and on each index, we compare
167   //     y then z. This is slow as the data is columnar and we need to
168   //     repeatedly branch inside each column.
169   //  2) we can stably sort first on x desc and then sort on y asc. This will
170   //     first put all the x in the correct order such that when we sort on
171   //     y asc, we will have the correct order of x where y is the same (since
172   //     the sort is stable).
173   //
174   // TODO(lalitm): it is possible that we could sort the last constraint (i.e.
175   // the first constraint in the below loop) in a non-stable way. However, this
176   // is more subtle than it appears as we would then need special handling where
177   // there are order bys on a column which is already sorted (e.g. ts, id).
178   // Investigate whether the performance gains from this are worthwhile. This
179   // also needs changes to the constraint modification logic in DbSqliteTable
180   // which currently eliminates constraints on sorted columns.
181   for (auto it = ob.rbegin(); it != ob.rend(); ++it) {
182     // Reset the index to the payload at the start of each iote
183     for (uint32_t i = 0; i < out.size(); ++i) {
184       rows[i].index = rows[i].payload;
185     }
186     table->ChainForColumn(it->col_idx)
187         .StableSort(rows.data(), rows.data() + rows.size(),
188                     it->desc
189                         ? column::DataLayerChain::SortDirection::kDescending
190                         : column::DataLayerChain::SortDirection::kAscending);
191   }
192 
193   // Recapture the payload from each of the sort tokens whose order now
194   // indicates the order
195   for (uint32_t i = 0; i < out.size(); ++i) {
196     out[i] = rows[i].payload;
197   }
198 }
199 
BoundedColumnFilterForTesting(const Constraint & c,const column::DataLayerChain & col,RowMap * rm)200 void QueryExecutor::BoundedColumnFilterForTesting(
201     const Constraint& c,
202     const column::DataLayerChain& col,
203     RowMap* rm) {
204   LinearSearch(c, col, rm);
205 }
206 
IndexedColumnFilterForTesting(const Constraint & c,const column::DataLayerChain & col,RowMap * rm)207 void QueryExecutor::IndexedColumnFilterForTesting(
208     const Constraint& c,
209     const column::DataLayerChain& col,
210     RowMap* rm) {
211   IndexSearch(c, col, rm);
212 }
213 
214 }  // namespace perfetto::trace_processor
215