ca6cd7673c1b59f727a73dbb44f397b3cef76668
[ep-engine.git] / benchmarks / access_scanner_bench.cc
1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2017 Couchbase, Inc
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 <access_scanner.h>
19 #include <benchmark/benchmark.h>
20 #include <fakes/fake_executorpool.h>
21 #include <mock/mock_synchronous_ep_engine.h>
22 #include <programs/engine_testapp/mock_server.h>
23 #include "benchmark_memory_tracker.h"
24 #include "dcp/dcpconnmap.h"
25
26 class EngineFixture : public benchmark::Fixture {
27 protected:
28     void SetUp(const benchmark::State& state) override {
29         SingleThreadedExecutorPool::replaceExecutorPoolWithFake();
30         executorPool = reinterpret_cast<SingleThreadedExecutorPool*>(
31                 ExecutorPool::get());
32         memoryTracker = BenchmarkMemoryTracker::getInstance(
33                 *get_mock_server_api()->alloc_hooks);
34         memoryTracker->reset();
35         std::string config = "dbname=benchmarks-test;" + varConfig;
36
37         engine.reset(new SynchronousEPEngine(config));
38         ObjectRegistry::onSwitchThread(engine.get());
39
40         engine->setKVBucket(
41                 engine->public_makeBucket(engine->getConfiguration()));
42
43         engine->public_initializeEngineCallbacks();
44         initialize_time_functions(get_mock_server_api()->core);
45         cookie = create_mock_cookie();
46     }
47
48     void TearDown(const benchmark::State& state) override {
49         executorPool->cancelAndClearAll();
50         destroy_mock_cookie(cookie);
51         destroy_mock_event_callbacks();
52         engine->getDcpConnMap().manageConnections();
53         engine.reset();
54         ObjectRegistry::onSwitchThread(nullptr);
55         ExecutorPool::shutdown();
56         memoryTracker->destroyInstance();
57     }
58
59     Item make_item(uint16_t vbid,
60                    const std::string& key,
61                    const std::string& value) {
62         uint8_t ext_meta[EXT_META_LEN] = {PROTOCOL_BINARY_DATATYPE_JSON};
63         Item item({key, DocNamespace::DefaultCollection},
64                   /*flags*/ 0,
65                   /*exp*/ 0,
66                   value.c_str(),
67                   value.size(),
68                   ext_meta,
69                   sizeof(ext_meta));
70         item.setVBucketId(vbid);
71         return item;
72     }
73
74     std::unique_ptr<SynchronousEPEngine> engine;
75     const void* cookie = nullptr;
76     const int vbid = 0;
77
78     // Allows subclasses to add stuff to the config
79     std::string varConfig;
80     BenchmarkMemoryTracker* memoryTracker;
81     SingleThreadedExecutorPool* executorPool;
82 };
83
84 class AccessLogBenchEngine : public EngineFixture {
85 protected:
86     void SetUp(const benchmark::State& state) override {
87         // If the access scanner is running then it will always scan
88         varConfig = "alog_resident_ratio_threshold=100;";
89         varConfig += "alog_max_stored_items=" +
90                      std::to_string(alog_max_stored_items);
91         EngineFixture::SetUp(state);
92     }
93
94     const size_t alog_max_stored_items = 2048;
95 };
96
97 ProcessClock::time_point runNextTask(SingleThreadedExecutorPool* pool,
98                                      task_type_t taskType,
99                                      std::string expectedTask) {
100     CheckedExecutor executor(pool, *pool->getLpTaskQ()[taskType]);
101     executor.runCurrentTask(expectedTask);
102     return executor.completeCurrentTask();
103 }
104
105 /*
106  * Varies whether the access scanner is running or not. Also varies the
107  * number of items stored in the vbucket. The purpose of this benchmark is to
108  * measure the maximum memory usage of the access scanner.
109  * Variables:
110  *  - range(0) : Whether to run access scanner constantly (0: no, 1: yes)
111  *  - range(1) : The number of items to fill the vbucket with
112  *
113  */
114 BENCHMARK_DEFINE_F(AccessLogBenchEngine, MemoryOverhead)
115 (benchmark::State& state) {
116     ExTask task = nullptr;
117     engine->getKVBucket()->setVBucketState(0, vbucket_state_active, false);
118     if (state.range(0) == 1) {
119         state.SetLabel("AccessScanner");
120         task = make_STRCPtr<AccessScanner>(
121                 *(engine->getKVBucket()), engine->getEpStats(), 1000);
122         ExecutorPool::get()->schedule(task);
123     } else {
124         state.SetLabel("Control");
125     }
126
127     // The content of each doc, nothing too large
128     std::string value(200, 'x');
129
130     // We have a key prefix so that our keys are more realistic in length
131     std::string keyPrefixPre(20, 'a');
132
133     for (int i = 0; i < state.range(1); ++i) {
134         auto item = make_item(vbid, keyPrefixPre + std::to_string(i), value);
135         engine->getKVBucket()->set(item, cookie);
136     }
137     size_t baseMemory = memoryTracker->getCurrentAlloc();
138     while (state.KeepRunning()) {
139         if (state.range(0) == 1) {
140             executorPool->wake(task->getId());
141             runNextTask(executorPool,
142                         AUXIO_TASK_IDX,
143                         "Generating access "
144                         "log");
145             runNextTask(executorPool,
146                         AUXIO_TASK_IDX,
147                         "Item Access Scanner on"
148                         " vb 0");
149         }
150     }
151     state.counters["MaxBytesAllocatedPerItem"] =
152             (memoryTracker->getMaxAlloc() - baseMemory) / alog_max_stored_items;
153 }
154
155 static void AccessScannerArguments(benchmark::internal::Benchmark* b) {
156     std::array<int, 2> numItems{{32768, 65536}};
157     for (int j : numItems) {
158         b->ArgPair(0, j);
159         b->ArgPair(1, j);
160     }
161 }
162
163 BENCHMARK_REGISTER_F(AccessLogBenchEngine, MemoryOverhead)
164         ->Apply(AccessScannerArguments)
165         ->MinTime(0.000001);
166
167 static char allow_no_stats_env[] = "ALLOW_NO_STATS_UPDATE=yeah";
168 int main(int argc, char** argv) {
169     putenv(allow_no_stats_env);
170     mock_init_alloc_hooks();
171     init_mock_server(true);
172     HashTable::setDefaultNumLocks(47);
173     initialize_time_functions(get_mock_server_api()->core);
174     ::benchmark::Initialize(&argc, argv);
175     ::benchmark::RunSpecifiedBenchmarks();
176     ::testing::InitGoogleTest(&argc, argv);
177     return RUN_ALL_TESTS();
178 }