• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Benchmark for the SQLite VTable interface.
16 // This benchmark measures the speed-of-light obtainable through a SQLite
17 // virtual table. The code here implements an ideal virtual table which fetches
18 // data in blocks and serves the xNext/xCol requests by just advancing a pointer
19 // in a buffer. This is to have a fair estimate w.r.t. cache-misses and pointer
20 // chasing of what an upper-bound can be for a virtual table implementation.
21 
22 #include <array>
23 #include <random>
24 
25 #include <benchmark/benchmark.h>
26 #include <sqlite3.h>
27 
28 #include "perfetto/base/compiler.h"
29 #include "src/trace_processor/sqlite/scoped_db.h"
30 
31 namespace {
32 
33 using benchmark::Counter;
34 using perfetto::trace_processor::ScopedDb;
35 using perfetto::trace_processor::ScopedStmt;
36 
IsBenchmarkFunctionalOnly()37 bool IsBenchmarkFunctionalOnly() {
38   return getenv("BENCHMARK_FUNCTIONAL_TEST_ONLY") != nullptr;
39 }
40 
BenchmarkArgs(benchmark::internal::Benchmark * b)41 void BenchmarkArgs(benchmark::internal::Benchmark* b) {
42   if (IsBenchmarkFunctionalOnly()) {
43     b->Ranges({{1024, 1024}, {1, 1}});
44   } else {
45     b->RangeMultiplier(2)->Ranges({{1024, 1024 * 128}, {1, 8}});
46   }
47 }
48 
49 class BenchmarkCursor : public sqlite3_vtab_cursor {
50  public:
BenchmarkCursor(size_t num_cols,size_t batch_size)51   explicit BenchmarkCursor(size_t num_cols, size_t batch_size)
52       : num_cols_(num_cols), batch_size_(batch_size), rnd_engine_(kRandomSeed) {
53     column_buffer_.resize(num_cols);
54     for (auto& col : column_buffer_)
55       col.resize(batch_size);
56     RandomFill();
57   }
58   PERFETTO_NO_INLINE int Next();
59   PERFETTO_NO_INLINE int Column(sqlite3_context* ctx, int);
60   PERFETTO_NO_INLINE int Eof();
61   void RandomFill();
62 
63  private:
64   size_t num_cols_;
65   size_t batch_size_;
66   static constexpr uint32_t kRandomSeed = 476;
67 
68   uint32_t row_ = 0;
69   using ColBatch = std::vector<int64_t>;
70   std::vector<ColBatch> column_buffer_;
71 
72   std::minstd_rand0 rnd_engine_;
73 };
74 
RandomFill()75 void BenchmarkCursor::RandomFill() {
76   for (size_t col = 0; col < num_cols_; col++) {
77     for (size_t row = 0; row < batch_size_; row++) {
78       column_buffer_[col][row] = static_cast<int64_t>(rnd_engine_());
79     }
80   }
81 }
82 
Next()83 int BenchmarkCursor::Next() {
84   row_ = (row_ + 1) % batch_size_;
85   if (row_ == 0)
86     RandomFill();
87   return SQLITE_OK;
88 }
89 
Eof()90 int BenchmarkCursor::Eof() {
91   return false;
92 }
93 
Column(sqlite3_context * ctx,int col_int)94 int BenchmarkCursor::Column(sqlite3_context* ctx, int col_int) {
95   const auto col = static_cast<size_t>(col_int);
96   PERFETTO_CHECK(col < column_buffer_.size());
97   sqlite3_result_int64(ctx, column_buffer_[col][row_]);
98   return SQLITE_OK;
99 }
100 
BM_SqliteStepAndResult(benchmark::State & state)101 static void BM_SqliteStepAndResult(benchmark::State& state) {
102   struct BenchmarkVtab : public sqlite3_vtab {
103     size_t num_cols;
104     size_t batch_size;
105   };
106 
107   sqlite3_initialize();
108 
109   // Make sure the module outlives the ScopedDb. SQLite calls xDisconnect in
110   // the database close function and so this struct needs to be available then.
111   sqlite3_module module{};
112 
113   ScopedDb db;
114   sqlite3* raw_db = nullptr;
115   PERFETTO_CHECK(sqlite3_open(":memory:", &raw_db) == SQLITE_OK);
116   db.reset(raw_db);
117 
118   auto create_fn = [](sqlite3* xdb, void* aux, int, const char* const*,
119                       sqlite3_vtab** tab, char**) {
120     benchmark::State& _state = *static_cast<benchmark::State*>(aux);
121     size_t num_cols = static_cast<size_t>(_state.range(1));
122     std::string sql = "CREATE TABLE x(";
123     for (size_t col = 0; col < num_cols; col++)
124       sql += "c" + std::to_string(col) + " BIG INT,";
125     sql[sql.size() - 1] = ')';
126     int res = sqlite3_declare_vtab(xdb, sql.c_str());
127     PERFETTO_CHECK(res == SQLITE_OK);
128     auto* vtab = new BenchmarkVtab();
129     vtab->num_cols = num_cols;
130     vtab->batch_size = num_cols;
131     *tab = vtab;
132     return SQLITE_OK;
133   };
134 
135   auto destroy_fn = [](sqlite3_vtab* t) {
136     delete static_cast<BenchmarkVtab*>(t);
137     return SQLITE_OK;
138   };
139 
140   module.xCreate = create_fn;
141   module.xConnect = create_fn;
142   module.xDisconnect = destroy_fn;
143   module.xDestroy = destroy_fn;
144 
145   module.xOpen = [](sqlite3_vtab* tab, sqlite3_vtab_cursor** c) {
146     auto* vtab = static_cast<BenchmarkVtab*>(tab);
147     *c = new BenchmarkCursor(vtab->num_cols, vtab->batch_size);
148     return SQLITE_OK;
149   };
150   module.xBestIndex = [](sqlite3_vtab*, sqlite3_index_info* idx) {
151     idx->orderByConsumed = true;
152     for (int i = 0; i < idx->nConstraint; ++i) {
153       idx->aConstraintUsage[i].omit = true;
154     }
155     return SQLITE_OK;
156   };
157   module.xClose = [](sqlite3_vtab_cursor* c) {
158     delete static_cast<BenchmarkCursor*>(c);
159     return SQLITE_OK;
160   };
161   module.xFilter = [](sqlite3_vtab_cursor*, int, const char*, int,
162                       sqlite3_value**) { return SQLITE_OK; };
163   module.xNext = [](sqlite3_vtab_cursor* c) {
164     return static_cast<BenchmarkCursor*>(c)->Next();
165   };
166   module.xEof = [](sqlite3_vtab_cursor* c) {
167     return static_cast<BenchmarkCursor*>(c)->Eof();
168   };
169   module.xColumn = [](sqlite3_vtab_cursor* c, sqlite3_context* a, int b) {
170     return static_cast<BenchmarkCursor*>(c)->Column(a, b);
171   };
172   int res =
173       sqlite3_create_module_v2(*db, "benchmark", &module, &state, nullptr);
174   PERFETTO_CHECK(res == SQLITE_OK);
175 
176   ScopedStmt stmt;
177   sqlite3_stmt* raw_stmt;
178   std::string sql = "SELECT * from benchmark";
179   int err = sqlite3_prepare_v2(*db, sql.c_str(), static_cast<int>(sql.size()),
180                                &raw_stmt, nullptr);
181   PERFETTO_CHECK(err == SQLITE_OK);
182   stmt.reset(raw_stmt);
183   size_t batch_size = static_cast<size_t>(state.range(0));
184   size_t num_cols = static_cast<size_t>(state.range(1));
185 
186   int64_t value = 0;
187   for (auto _ : state) {
188     for (size_t i = 0; i < batch_size; i++) {
189       PERFETTO_CHECK(sqlite3_step(*stmt) == SQLITE_ROW);
190       for (int col = 0; col < static_cast<int>(num_cols); col++) {
191         value ^= sqlite3_column_int64(*stmt, col);
192       }
193     }
194     PERFETTO_CHECK(value != 42);
195   }
196 
197   state.counters["rows"] = Counter(static_cast<double>(batch_size),
198                                    Counter::kIsIterationInvariantRate);
199 }
200 
201 BENCHMARK(BM_SqliteStepAndResult)->Apply(BenchmarkArgs);
202 
203 }  // namespace
204