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