]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/db/perf_context_test.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / rocksdb / db / perf_context_test.cc
CommitLineData
7c673cae 1// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
11fdf7f2
TL
2// This source code is licensed under both the GPLv2 (found in the
3// COPYING file in the root directory) and Apache 2.0 License
4// (found in the LICENSE.Apache file in the root directory).
7c673cae
FG
5//
6#include <algorithm>
7#include <iostream>
8#include <thread>
9#include <vector>
10
11#include "monitoring/histogram.h"
12#include "monitoring/instrumented_mutex.h"
494da23a 13#include "monitoring/perf_context_imp.h"
7c673cae
FG
14#include "monitoring/thread_status_util.h"
15#include "port/port.h"
16#include "rocksdb/db.h"
17#include "rocksdb/memtablerep.h"
18#include "rocksdb/perf_context.h"
19#include "rocksdb/slice_transform.h"
f67539c2 20#include "test_util/testharness.h"
7c673cae
FG
21#include "util/stop_watch.h"
22#include "util/string_util.h"
7c673cae
FG
23#include "utilities/merge_operators.h"
24
25bool FLAGS_random_key = false;
26bool FLAGS_use_set_based_memetable = false;
27int FLAGS_total_keys = 100;
28int FLAGS_write_buffer_size = 1000000000;
29int FLAGS_max_write_buffer_number = 8;
30int FLAGS_min_write_buffer_number_to_merge = 7;
31bool FLAGS_verbose = false;
32
33// Path to the database on file system
f67539c2
TL
34const std::string kDbName =
35 ROCKSDB_NAMESPACE::test::PerThreadDBPath("perf_context_test");
7c673cae 36
f67539c2 37namespace ROCKSDB_NAMESPACE {
7c673cae
FG
38
39std::shared_ptr<DB> OpenDb(bool read_only = false) {
40 DB* db;
41 Options options;
42 options.create_if_missing = true;
43 options.max_open_files = -1;
44 options.write_buffer_size = FLAGS_write_buffer_size;
45 options.max_write_buffer_number = FLAGS_max_write_buffer_number;
46 options.min_write_buffer_number_to_merge =
47 FLAGS_min_write_buffer_number_to_merge;
48
49 if (FLAGS_use_set_based_memetable) {
50#ifndef ROCKSDB_LITE
f67539c2
TL
51 options.prefix_extractor.reset(
52 ROCKSDB_NAMESPACE::NewFixedPrefixTransform(0));
7c673cae
FG
53 options.memtable_factory.reset(NewHashSkipListRepFactory());
54#endif // ROCKSDB_LITE
55 }
56
57 Status s;
58 if (!read_only) {
59 s = DB::Open(options, kDbName, &db);
60 } else {
61 s = DB::OpenForReadOnly(options, kDbName, &db);
62 }
63 EXPECT_OK(s);
64 return std::shared_ptr<DB>(db);
65}
66
67class PerfContextTest : public testing::Test {};
68
69TEST_F(PerfContextTest, SeekIntoDeletion) {
70 DestroyDB(kDbName, Options());
71 auto db = OpenDb();
72 WriteOptions write_options;
73 ReadOptions read_options;
74
75 for (int i = 0; i < FLAGS_total_keys; ++i) {
76 std::string key = "k" + ToString(i);
77 std::string value = "v" + ToString(i);
78
79 db->Put(write_options, key, value);
80 }
81
82 for (int i = 0; i < FLAGS_total_keys -1 ; ++i) {
83 std::string key = "k" + ToString(i);
84 db->Delete(write_options, key);
85 }
86
87 HistogramImpl hist_get;
88 HistogramImpl hist_get_time;
89 for (int i = 0; i < FLAGS_total_keys - 1; ++i) {
90 std::string key = "k" + ToString(i);
91 std::string value;
92
11fdf7f2 93 get_perf_context()->Reset();
7c673cae
FG
94 StopWatchNano timer(Env::Default());
95 timer.Start();
96 auto status = db->Get(read_options, key, &value);
97 auto elapsed_nanos = timer.ElapsedNanos();
98 ASSERT_TRUE(status.IsNotFound());
11fdf7f2 99 hist_get.Add(get_perf_context()->user_key_comparison_count);
7c673cae
FG
100 hist_get_time.Add(elapsed_nanos);
101 }
102
103 if (FLAGS_verbose) {
104 std::cout << "Get user key comparison: \n" << hist_get.ToString()
105 << "Get time: \n" << hist_get_time.ToString();
106 }
107
108 {
109 HistogramImpl hist_seek_to_first;
110 std::unique_ptr<Iterator> iter(db->NewIterator(read_options));
111
11fdf7f2 112 get_perf_context()->Reset();
7c673cae
FG
113 StopWatchNano timer(Env::Default(), true);
114 iter->SeekToFirst();
11fdf7f2 115 hist_seek_to_first.Add(get_perf_context()->user_key_comparison_count);
7c673cae
FG
116 auto elapsed_nanos = timer.ElapsedNanos();
117
118 if (FLAGS_verbose) {
119 std::cout << "SeekToFirst uesr key comparison: \n"
120 << hist_seek_to_first.ToString()
11fdf7f2 121 << "ikey skipped: " << get_perf_context()->internal_key_skipped_count
7c673cae
FG
122 << "\n"
123 << "idelete skipped: "
11fdf7f2 124 << get_perf_context()->internal_delete_skipped_count << "\n"
7c673cae
FG
125 << "elapsed: " << elapsed_nanos << "\n";
126 }
127 }
128
129 HistogramImpl hist_seek;
130 for (int i = 0; i < FLAGS_total_keys; ++i) {
131 std::unique_ptr<Iterator> iter(db->NewIterator(read_options));
132 std::string key = "k" + ToString(i);
133
11fdf7f2 134 get_perf_context()->Reset();
7c673cae
FG
135 StopWatchNano timer(Env::Default(), true);
136 iter->Seek(key);
137 auto elapsed_nanos = timer.ElapsedNanos();
11fdf7f2 138 hist_seek.Add(get_perf_context()->user_key_comparison_count);
7c673cae 139 if (FLAGS_verbose) {
11fdf7f2
TL
140 std::cout << "seek cmp: " << get_perf_context()->user_key_comparison_count
141 << " ikey skipped " << get_perf_context()->internal_key_skipped_count
7c673cae 142 << " idelete skipped "
11fdf7f2 143 << get_perf_context()->internal_delete_skipped_count
7c673cae
FG
144 << " elapsed: " << elapsed_nanos << "ns\n";
145 }
146
11fdf7f2 147 get_perf_context()->Reset();
7c673cae
FG
148 ASSERT_TRUE(iter->Valid());
149 StopWatchNano timer2(Env::Default(), true);
150 iter->Next();
151 auto elapsed_nanos2 = timer2.ElapsedNanos();
152 if (FLAGS_verbose) {
11fdf7f2 153 std::cout << "next cmp: " << get_perf_context()->user_key_comparison_count
7c673cae
FG
154 << "elapsed: " << elapsed_nanos2 << "ns\n";
155 }
156 }
157
158 if (FLAGS_verbose) {
159 std::cout << "Seek uesr key comparison: \n" << hist_seek.ToString();
160 }
161}
162
163TEST_F(PerfContextTest, StopWatchNanoOverhead) {
164 // profile the timer cost by itself!
165 const int kTotalIterations = 1000000;
166 std::vector<uint64_t> timings(kTotalIterations);
167
168 StopWatchNano timer(Env::Default(), true);
169 for (auto& timing : timings) {
170 timing = timer.ElapsedNanos(true /* reset */);
171 }
172
173 HistogramImpl histogram;
174 for (const auto timing : timings) {
175 histogram.Add(timing);
176 }
177
178 if (FLAGS_verbose) {
179 std::cout << histogram.ToString();
180 }
181}
182
183TEST_F(PerfContextTest, StopWatchOverhead) {
184 // profile the timer cost by itself!
185 const int kTotalIterations = 1000000;
186 uint64_t elapsed = 0;
187 std::vector<uint64_t> timings(kTotalIterations);
188
189 StopWatch timer(Env::Default(), nullptr, 0, &elapsed);
190 for (auto& timing : timings) {
191 timing = elapsed;
192 }
193
194 HistogramImpl histogram;
195 uint64_t prev_timing = 0;
196 for (const auto timing : timings) {
197 histogram.Add(timing - prev_timing);
198 prev_timing = timing;
199 }
200
201 if (FLAGS_verbose) {
202 std::cout << histogram.ToString();
203 }
204}
205
206void ProfileQueries(bool enabled_time = false) {
207 DestroyDB(kDbName, Options()); // Start this test with a fresh DB
208
209 auto db = OpenDb();
210
211 WriteOptions write_options;
212 ReadOptions read_options;
213
214 HistogramImpl hist_put;
215
216 HistogramImpl hist_get;
217 HistogramImpl hist_get_snapshot;
218 HistogramImpl hist_get_memtable;
219 HistogramImpl hist_get_files;
220 HistogramImpl hist_get_post_process;
221 HistogramImpl hist_num_memtable_checked;
222
223 HistogramImpl hist_mget;
224 HistogramImpl hist_mget_snapshot;
225 HistogramImpl hist_mget_memtable;
226 HistogramImpl hist_mget_files;
227 HistogramImpl hist_mget_post_process;
228 HistogramImpl hist_mget_num_memtable_checked;
229
230 HistogramImpl hist_write_pre_post;
231 HistogramImpl hist_write_wal_time;
232 HistogramImpl hist_write_memtable_time;
11fdf7f2
TL
233 HistogramImpl hist_write_delay_time;
234 HistogramImpl hist_write_thread_wait_nanos;
235 HistogramImpl hist_write_scheduling_time;
7c673cae
FG
236
237 uint64_t total_db_mutex_nanos = 0;
238
239 if (FLAGS_verbose) {
240 std::cout << "Inserting " << FLAGS_total_keys << " key/value pairs\n...\n";
241 }
242
243 std::vector<int> keys;
244 const int kFlushFlag = -1;
245 for (int i = 0; i < FLAGS_total_keys; ++i) {
246 keys.push_back(i);
247 if (i == FLAGS_total_keys / 2) {
248 // Issuing a flush in the middle.
249 keys.push_back(kFlushFlag);
250 }
251 }
252
253 if (FLAGS_random_key) {
254 std::random_shuffle(keys.begin(), keys.end());
255 }
256#ifndef NDEBUG
257 ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 1U);
258#endif
259 int num_mutex_waited = 0;
260 for (const int i : keys) {
261 if (i == kFlushFlag) {
262 FlushOptions fo;
263 db->Flush(fo);
264 continue;
265 }
266
267 std::string key = "k" + ToString(i);
268 std::string value = "v" + ToString(i);
269
270 std::vector<std::string> values;
271
11fdf7f2 272 get_perf_context()->Reset();
7c673cae
FG
273 db->Put(write_options, key, value);
274 if (++num_mutex_waited > 3) {
275#ifndef NDEBUG
276 ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0U);
277#endif
278 }
11fdf7f2
TL
279 hist_write_pre_post.Add(
280 get_perf_context()->write_pre_and_post_process_time);
281 hist_write_wal_time.Add(get_perf_context()->write_wal_time);
282 hist_write_memtable_time.Add(get_perf_context()->write_memtable_time);
283 hist_write_delay_time.Add(get_perf_context()->write_delay_time);
284 hist_write_thread_wait_nanos.Add(
285 get_perf_context()->write_thread_wait_nanos);
286 hist_write_scheduling_time.Add(
287 get_perf_context()->write_scheduling_flushes_compactions_time);
288 hist_put.Add(get_perf_context()->user_key_comparison_count);
289 total_db_mutex_nanos += get_perf_context()->db_mutex_lock_nanos;
7c673cae
FG
290 }
291#ifndef NDEBUG
292 ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0U);
293#endif
294
295 for (const int i : keys) {
296 if (i == kFlushFlag) {
297 continue;
298 }
299 std::string key = "k" + ToString(i);
300 std::string expected_value = "v" + ToString(i);
301 std::string value;
302
303 std::vector<Slice> multiget_keys = {Slice(key)};
304 std::vector<std::string> values;
305
11fdf7f2 306 get_perf_context()->Reset();
7c673cae
FG
307 ASSERT_OK(db->Get(read_options, key, &value));
308 ASSERT_EQ(expected_value, value);
11fdf7f2
TL
309 hist_get_snapshot.Add(get_perf_context()->get_snapshot_time);
310 hist_get_memtable.Add(get_perf_context()->get_from_memtable_time);
311 hist_get_files.Add(get_perf_context()->get_from_output_files_time);
312 hist_num_memtable_checked.Add(get_perf_context()->get_from_memtable_count);
313 hist_get_post_process.Add(get_perf_context()->get_post_process_time);
314 hist_get.Add(get_perf_context()->user_key_comparison_count);
315
316 get_perf_context()->Reset();
7c673cae 317 db->MultiGet(read_options, multiget_keys, &values);
11fdf7f2
TL
318 hist_mget_snapshot.Add(get_perf_context()->get_snapshot_time);
319 hist_mget_memtable.Add(get_perf_context()->get_from_memtable_time);
320 hist_mget_files.Add(get_perf_context()->get_from_output_files_time);
321 hist_mget_num_memtable_checked.Add(get_perf_context()->get_from_memtable_count);
322 hist_mget_post_process.Add(get_perf_context()->get_post_process_time);
323 hist_mget.Add(get_perf_context()->user_key_comparison_count);
7c673cae
FG
324 }
325
326 if (FLAGS_verbose) {
327 std::cout << "Put uesr key comparison: \n" << hist_put.ToString()
328 << "Get uesr key comparison: \n" << hist_get.ToString()
329 << "MultiGet uesr key comparison: \n" << hist_get.ToString();
330 std::cout << "Put(): Pre and Post Process Time: \n"
331 << hist_write_pre_post.ToString() << " Writing WAL time: \n"
332 << hist_write_wal_time.ToString() << "\n"
333 << " Writing Mem Table time: \n"
334 << hist_write_memtable_time.ToString() << "\n"
11fdf7f2
TL
335 << " Write Delay: \n" << hist_write_delay_time.ToString() << "\n"
336 << " Waiting for Batch time: \n"
337 << hist_write_thread_wait_nanos.ToString() << "\n"
338 << " Scheduling Flushes and Compactions Time: \n"
339 << hist_write_scheduling_time.ToString() << "\n"
7c673cae
FG
340 << " Total DB mutex nanos: \n" << total_db_mutex_nanos << "\n";
341
342 std::cout << "Get(): Time to get snapshot: \n"
343 << hist_get_snapshot.ToString()
344 << " Time to get value from memtables: \n"
345 << hist_get_memtable.ToString() << "\n"
346 << " Time to get value from output files: \n"
347 << hist_get_files.ToString() << "\n"
348 << " Number of memtables checked: \n"
349 << hist_num_memtable_checked.ToString() << "\n"
350 << " Time to post process: \n" << hist_get_post_process.ToString()
351 << "\n";
352
353 std::cout << "MultiGet(): Time to get snapshot: \n"
354 << hist_mget_snapshot.ToString()
355 << " Time to get value from memtables: \n"
356 << hist_mget_memtable.ToString() << "\n"
357 << " Time to get value from output files: \n"
358 << hist_mget_files.ToString() << "\n"
359 << " Number of memtables checked: \n"
360 << hist_mget_num_memtable_checked.ToString() << "\n"
361 << " Time to post process: \n"
362 << hist_mget_post_process.ToString() << "\n";
363 }
364
365 if (enabled_time) {
366 ASSERT_GT(hist_get.Average(), 0);
367 ASSERT_GT(hist_get_snapshot.Average(), 0);
368 ASSERT_GT(hist_get_memtable.Average(), 0);
369 ASSERT_GT(hist_get_files.Average(), 0);
370 ASSERT_GT(hist_get_post_process.Average(), 0);
371 ASSERT_GT(hist_num_memtable_checked.Average(), 0);
372
373 ASSERT_GT(hist_mget.Average(), 0);
374 ASSERT_GT(hist_mget_snapshot.Average(), 0);
375 ASSERT_GT(hist_mget_memtable.Average(), 0);
376 ASSERT_GT(hist_mget_files.Average(), 0);
377 ASSERT_GT(hist_mget_post_process.Average(), 0);
378 ASSERT_GT(hist_mget_num_memtable_checked.Average(), 0);
11fdf7f2
TL
379
380 EXPECT_GT(hist_write_pre_post.Average(), 0);
381 EXPECT_GT(hist_write_wal_time.Average(), 0);
382 EXPECT_GT(hist_write_memtable_time.Average(), 0);
383 EXPECT_EQ(hist_write_delay_time.Average(), 0);
384 EXPECT_EQ(hist_write_thread_wait_nanos.Average(), 0);
385 EXPECT_GT(hist_write_scheduling_time.Average(), 0);
386
7c673cae
FG
387#ifndef NDEBUG
388 ASSERT_GT(total_db_mutex_nanos, 2000U);
389#endif
390 }
391
392 db.reset();
393 db = OpenDb(true);
394
395 hist_get.Clear();
396 hist_get_snapshot.Clear();
397 hist_get_memtable.Clear();
398 hist_get_files.Clear();
399 hist_get_post_process.Clear();
400 hist_num_memtable_checked.Clear();
401
402 hist_mget.Clear();
403 hist_mget_snapshot.Clear();
404 hist_mget_memtable.Clear();
405 hist_mget_files.Clear();
406 hist_mget_post_process.Clear();
407 hist_mget_num_memtable_checked.Clear();
408
409 for (const int i : keys) {
410 if (i == kFlushFlag) {
411 continue;
412 }
413 std::string key = "k" + ToString(i);
414 std::string expected_value = "v" + ToString(i);
415 std::string value;
416
417 std::vector<Slice> multiget_keys = {Slice(key)};
418 std::vector<std::string> values;
419
11fdf7f2 420 get_perf_context()->Reset();
7c673cae
FG
421 ASSERT_OK(db->Get(read_options, key, &value));
422 ASSERT_EQ(expected_value, value);
11fdf7f2
TL
423 hist_get_snapshot.Add(get_perf_context()->get_snapshot_time);
424 hist_get_memtable.Add(get_perf_context()->get_from_memtable_time);
425 hist_get_files.Add(get_perf_context()->get_from_output_files_time);
426 hist_num_memtable_checked.Add(get_perf_context()->get_from_memtable_count);
427 hist_get_post_process.Add(get_perf_context()->get_post_process_time);
428 hist_get.Add(get_perf_context()->user_key_comparison_count);
429
430 get_perf_context()->Reset();
7c673cae 431 db->MultiGet(read_options, multiget_keys, &values);
11fdf7f2
TL
432 hist_mget_snapshot.Add(get_perf_context()->get_snapshot_time);
433 hist_mget_memtable.Add(get_perf_context()->get_from_memtable_time);
434 hist_mget_files.Add(get_perf_context()->get_from_output_files_time);
435 hist_mget_num_memtable_checked.Add(get_perf_context()->get_from_memtable_count);
436 hist_mget_post_process.Add(get_perf_context()->get_post_process_time);
437 hist_mget.Add(get_perf_context()->user_key_comparison_count);
7c673cae
FG
438 }
439
440 if (FLAGS_verbose) {
441 std::cout << "ReadOnly Get uesr key comparison: \n" << hist_get.ToString()
442 << "ReadOnly MultiGet uesr key comparison: \n"
443 << hist_mget.ToString();
444
445 std::cout << "ReadOnly Get(): Time to get snapshot: \n"
446 << hist_get_snapshot.ToString()
447 << " Time to get value from memtables: \n"
448 << hist_get_memtable.ToString() << "\n"
449 << " Time to get value from output files: \n"
450 << hist_get_files.ToString() << "\n"
451 << " Number of memtables checked: \n"
452 << hist_num_memtable_checked.ToString() << "\n"
453 << " Time to post process: \n" << hist_get_post_process.ToString()
454 << "\n";
455
456 std::cout << "ReadOnly MultiGet(): Time to get snapshot: \n"
457 << hist_mget_snapshot.ToString()
458 << " Time to get value from memtables: \n"
459 << hist_mget_memtable.ToString() << "\n"
460 << " Time to get value from output files: \n"
461 << hist_mget_files.ToString() << "\n"
462 << " Number of memtables checked: \n"
463 << hist_mget_num_memtable_checked.ToString() << "\n"
464 << " Time to post process: \n"
465 << hist_mget_post_process.ToString() << "\n";
466 }
467
468 if (enabled_time) {
469 ASSERT_GT(hist_get.Average(), 0);
470 ASSERT_GT(hist_get_memtable.Average(), 0);
471 ASSERT_GT(hist_get_files.Average(), 0);
472 ASSERT_GT(hist_num_memtable_checked.Average(), 0);
473 // In read-only mode Get(), no super version operation is needed
474 ASSERT_EQ(hist_get_post_process.Average(), 0);
11fdf7f2 475 ASSERT_GT(hist_get_snapshot.Average(), 0);
7c673cae
FG
476
477 ASSERT_GT(hist_mget.Average(), 0);
478 ASSERT_GT(hist_mget_snapshot.Average(), 0);
479 ASSERT_GT(hist_mget_memtable.Average(), 0);
480 ASSERT_GT(hist_mget_files.Average(), 0);
481 ASSERT_GT(hist_mget_post_process.Average(), 0);
482 ASSERT_GT(hist_mget_num_memtable_checked.Average(), 0);
483 }
484}
485
486#ifndef ROCKSDB_LITE
487TEST_F(PerfContextTest, KeyComparisonCount) {
488 SetPerfLevel(kEnableCount);
489 ProfileQueries();
490
491 SetPerfLevel(kDisable);
492 ProfileQueries();
493
494 SetPerfLevel(kEnableTime);
495 ProfileQueries(true);
496}
497#endif // ROCKSDB_LITE
498
499// make perf_context_test
500// export ROCKSDB_TESTS=PerfContextTest.SeekKeyComparison
501// For one memtable:
502// ./perf_context_test --write_buffer_size=500000 --total_keys=10000
503// For two memtables:
504// ./perf_context_test --write_buffer_size=250000 --total_keys=10000
505// Specify --random_key=1 to shuffle the key before insertion
506// Results show that, for sequential insertion, worst-case Seek Key comparison
507// is close to the total number of keys (linear), when there is only one
508// memtable. When there are two memtables, even the avg Seek Key comparison
509// starts to become linear to the input size.
510
511TEST_F(PerfContextTest, SeekKeyComparison) {
512 DestroyDB(kDbName, Options());
513 auto db = OpenDb();
514 WriteOptions write_options;
515 ReadOptions read_options;
516
517 if (FLAGS_verbose) {
518 std::cout << "Inserting " << FLAGS_total_keys << " key/value pairs\n...\n";
519 }
520
521 std::vector<int> keys;
522 for (int i = 0; i < FLAGS_total_keys; ++i) {
523 keys.push_back(i);
524 }
525
526 if (FLAGS_random_key) {
527 std::random_shuffle(keys.begin(), keys.end());
528 }
529
530 HistogramImpl hist_put_time;
531 HistogramImpl hist_wal_time;
532 HistogramImpl hist_time_diff;
533
534 SetPerfLevel(kEnableTime);
535 StopWatchNano timer(Env::Default());
536 for (const int i : keys) {
537 std::string key = "k" + ToString(i);
538 std::string value = "v" + ToString(i);
539
11fdf7f2 540 get_perf_context()->Reset();
7c673cae
FG
541 timer.Start();
542 db->Put(write_options, key, value);
543 auto put_time = timer.ElapsedNanos();
544 hist_put_time.Add(put_time);
11fdf7f2
TL
545 hist_wal_time.Add(get_perf_context()->write_wal_time);
546 hist_time_diff.Add(put_time - get_perf_context()->write_wal_time);
7c673cae
FG
547 }
548
549 if (FLAGS_verbose) {
550 std::cout << "Put time:\n" << hist_put_time.ToString() << "WAL time:\n"
551 << hist_wal_time.ToString() << "time diff:\n"
552 << hist_time_diff.ToString();
553 }
554
555 HistogramImpl hist_seek;
556 HistogramImpl hist_next;
557
558 for (int i = 0; i < FLAGS_total_keys; ++i) {
559 std::string key = "k" + ToString(i);
560 std::string value = "v" + ToString(i);
561
562 std::unique_ptr<Iterator> iter(db->NewIterator(read_options));
11fdf7f2 563 get_perf_context()->Reset();
7c673cae
FG
564 iter->Seek(key);
565 ASSERT_TRUE(iter->Valid());
566 ASSERT_EQ(iter->value().ToString(), value);
11fdf7f2 567 hist_seek.Add(get_perf_context()->user_key_comparison_count);
7c673cae
FG
568 }
569
570 std::unique_ptr<Iterator> iter(db->NewIterator(read_options));
571 for (iter->SeekToFirst(); iter->Valid();) {
11fdf7f2 572 get_perf_context()->Reset();
7c673cae 573 iter->Next();
11fdf7f2 574 hist_next.Add(get_perf_context()->user_key_comparison_count);
7c673cae
FG
575 }
576
577 if (FLAGS_verbose) {
578 std::cout << "Seek:\n" << hist_seek.ToString() << "Next:\n"
579 << hist_next.ToString();
580 }
581}
582
583TEST_F(PerfContextTest, DBMutexLockCounter) {
584 int stats_code[] = {0, static_cast<int>(DB_MUTEX_WAIT_MICROS)};
494da23a 585 for (PerfLevel perf_level_test :
7c673cae
FG
586 {PerfLevel::kEnableTimeExceptForMutex, PerfLevel::kEnableTime}) {
587 for (int c = 0; c < 2; ++c) {
588 InstrumentedMutex mutex(nullptr, Env::Default(), stats_code[c]);
589 mutex.Lock();
f67539c2 590 ROCKSDB_NAMESPACE::port::Thread child_thread([&] {
494da23a 591 SetPerfLevel(perf_level_test);
11fdf7f2
TL
592 get_perf_context()->Reset();
593 ASSERT_EQ(get_perf_context()->db_mutex_lock_nanos, 0);
7c673cae
FG
594 mutex.Lock();
595 mutex.Unlock();
494da23a 596 if (perf_level_test == PerfLevel::kEnableTimeExceptForMutex ||
7c673cae 597 stats_code[c] != DB_MUTEX_WAIT_MICROS) {
11fdf7f2 598 ASSERT_EQ(get_perf_context()->db_mutex_lock_nanos, 0);
7c673cae
FG
599 } else {
600 // increment the counter only when it's a DB Mutex
11fdf7f2 601 ASSERT_GT(get_perf_context()->db_mutex_lock_nanos, 0);
7c673cae
FG
602 }
603 });
604 Env::Default()->SleepForMicroseconds(100);
605 mutex.Unlock();
606 child_thread.join();
607 }
608 }
609}
610
611TEST_F(PerfContextTest, FalseDBMutexWait) {
612 SetPerfLevel(kEnableTime);
613 int stats_code[] = {0, static_cast<int>(DB_MUTEX_WAIT_MICROS)};
614 for (int c = 0; c < 2; ++c) {
615 InstrumentedMutex mutex(nullptr, Env::Default(), stats_code[c]);
616 InstrumentedCondVar lock(&mutex);
11fdf7f2 617 get_perf_context()->Reset();
7c673cae
FG
618 mutex.Lock();
619 lock.TimedWait(100);
620 mutex.Unlock();
621 if (stats_code[c] == static_cast<int>(DB_MUTEX_WAIT_MICROS)) {
622 // increment the counter only when it's a DB Mutex
11fdf7f2 623 ASSERT_GT(get_perf_context()->db_condition_wait_nanos, 0);
7c673cae 624 } else {
11fdf7f2 625 ASSERT_EQ(get_perf_context()->db_condition_wait_nanos, 0);
7c673cae
FG
626 }
627 }
628}
629
630TEST_F(PerfContextTest, ToString) {
11fdf7f2
TL
631 get_perf_context()->Reset();
632 get_perf_context()->block_read_count = 12345;
7c673cae 633
11fdf7f2 634 std::string zero_included = get_perf_context()->ToString();
7c673cae
FG
635 ASSERT_NE(std::string::npos, zero_included.find("= 0"));
636 ASSERT_NE(std::string::npos, zero_included.find("= 12345"));
637
11fdf7f2 638 std::string zero_excluded = get_perf_context()->ToString(true);
7c673cae
FG
639 ASSERT_EQ(std::string::npos, zero_excluded.find("= 0"));
640 ASSERT_NE(std::string::npos, zero_excluded.find("= 12345"));
641}
642
643TEST_F(PerfContextTest, MergeOperatorTime) {
644 DestroyDB(kDbName, Options());
645 DB* db;
646 Options options;
647 options.create_if_missing = true;
648 options.merge_operator = MergeOperators::CreateStringAppendOperator();
649 Status s = DB::Open(options, kDbName, &db);
650 EXPECT_OK(s);
651
652 std::string val;
653 ASSERT_OK(db->Merge(WriteOptions(), "k1", "val1"));
654 ASSERT_OK(db->Merge(WriteOptions(), "k1", "val2"));
655 ASSERT_OK(db->Merge(WriteOptions(), "k1", "val3"));
656 ASSERT_OK(db->Merge(WriteOptions(), "k1", "val4"));
657
658 SetPerfLevel(kEnableTime);
11fdf7f2 659 get_perf_context()->Reset();
7c673cae
FG
660 ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
661#ifdef OS_SOLARIS
662 for (int i = 0; i < 100; i++) {
663 ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
664 }
665#endif
11fdf7f2 666 EXPECT_GT(get_perf_context()->merge_operator_time_nanos, 0);
7c673cae
FG
667
668 ASSERT_OK(db->Flush(FlushOptions()));
669
11fdf7f2 670 get_perf_context()->Reset();
7c673cae
FG
671 ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
672#ifdef OS_SOLARIS
673 for (int i = 0; i < 100; i++) {
674 ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
675 }
676#endif
11fdf7f2 677 EXPECT_GT(get_perf_context()->merge_operator_time_nanos, 0);
7c673cae
FG
678
679 ASSERT_OK(db->CompactRange(CompactRangeOptions(), nullptr, nullptr));
680
11fdf7f2 681 get_perf_context()->Reset();
7c673cae
FG
682 ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
683#ifdef OS_SOLARIS
684 for (int i = 0; i < 100; i++) {
685 ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
686 }
687#endif
11fdf7f2 688 EXPECT_GT(get_perf_context()->merge_operator_time_nanos, 0);
7c673cae
FG
689
690 delete db;
691}
494da23a
TL
692
693TEST_F(PerfContextTest, CopyAndMove) {
694 // Assignment operator
695 {
696 get_perf_context()->Reset();
697 get_perf_context()->EnablePerLevelPerfContext();
698 PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 5);
699 ASSERT_EQ(
700 1,
701 (*(get_perf_context()->level_to_perf_context))[5].bloom_filter_useful);
702 PerfContext perf_context_assign;
703 perf_context_assign = *get_perf_context();
704 ASSERT_EQ(
705 1,
706 (*(perf_context_assign.level_to_perf_context))[5].bloom_filter_useful);
707 get_perf_context()->ClearPerLevelPerfContext();
708 get_perf_context()->Reset();
709 ASSERT_EQ(
710 1,
711 (*(perf_context_assign.level_to_perf_context))[5].bloom_filter_useful);
712 perf_context_assign.ClearPerLevelPerfContext();
713 perf_context_assign.Reset();
714 }
715 // Copy constructor
716 {
717 get_perf_context()->Reset();
718 get_perf_context()->EnablePerLevelPerfContext();
719 PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 5);
720 ASSERT_EQ(
721 1,
722 (*(get_perf_context()->level_to_perf_context))[5].bloom_filter_useful);
723 PerfContext perf_context_copy(*get_perf_context());
724 ASSERT_EQ(
725 1, (*(perf_context_copy.level_to_perf_context))[5].bloom_filter_useful);
726 get_perf_context()->ClearPerLevelPerfContext();
727 get_perf_context()->Reset();
728 ASSERT_EQ(
729 1, (*(perf_context_copy.level_to_perf_context))[5].bloom_filter_useful);
730 perf_context_copy.ClearPerLevelPerfContext();
731 perf_context_copy.Reset();
732 }
733 // Move constructor
734 {
735 get_perf_context()->Reset();
736 get_perf_context()->EnablePerLevelPerfContext();
737 PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 5);
738 ASSERT_EQ(
739 1,
740 (*(get_perf_context()->level_to_perf_context))[5].bloom_filter_useful);
741 PerfContext perf_context_move = std::move(*get_perf_context());
742 ASSERT_EQ(
743 1, (*(perf_context_move.level_to_perf_context))[5].bloom_filter_useful);
744 get_perf_context()->ClearPerLevelPerfContext();
745 get_perf_context()->Reset();
746 ASSERT_EQ(
747 1, (*(perf_context_move.level_to_perf_context))[5].bloom_filter_useful);
748 perf_context_move.ClearPerLevelPerfContext();
749 perf_context_move.Reset();
750 }
751}
752
753TEST_F(PerfContextTest, PerfContextDisableEnable) {
754 get_perf_context()->Reset();
755 get_perf_context()->EnablePerLevelPerfContext();
756 PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_full_positive, 1, 0);
757 get_perf_context()->DisablePerLevelPerfContext();
758 PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 5);
759 get_perf_context()->EnablePerLevelPerfContext();
760 PERF_COUNTER_BY_LEVEL_ADD(block_cache_hit_count, 1, 0);
761 get_perf_context()->DisablePerLevelPerfContext();
762 PerfContext perf_context_copy(*get_perf_context());
763 ASSERT_EQ(1, (*(perf_context_copy.level_to_perf_context))[0]
764 .bloom_filter_full_positive);
765 // this was set when per level perf context is disabled, should not be copied
766 ASSERT_NE(
767 1, (*(perf_context_copy.level_to_perf_context))[5].bloom_filter_useful);
768 ASSERT_EQ(
769 1, (*(perf_context_copy.level_to_perf_context))[0].block_cache_hit_count);
770 perf_context_copy.ClearPerLevelPerfContext();
771 perf_context_copy.Reset();
772 get_perf_context()->ClearPerLevelPerfContext();
773 get_perf_context()->Reset();
774}
775
776TEST_F(PerfContextTest, PerfContextByLevelGetSet) {
777 get_perf_context()->Reset();
778 get_perf_context()->EnablePerLevelPerfContext();
779 PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_full_positive, 1, 0);
780 PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 5);
781 PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 7);
782 PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 7);
783 PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_full_true_positive, 1, 2);
784 PERF_COUNTER_BY_LEVEL_ADD(block_cache_hit_count, 1, 0);
785 PERF_COUNTER_BY_LEVEL_ADD(block_cache_hit_count, 5, 2);
786 PERF_COUNTER_BY_LEVEL_ADD(block_cache_miss_count, 2, 3);
787 PERF_COUNTER_BY_LEVEL_ADD(block_cache_miss_count, 4, 1);
788 ASSERT_EQ(
789 0, (*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
790 ASSERT_EQ(
791 1, (*(get_perf_context()->level_to_perf_context))[5].bloom_filter_useful);
792 ASSERT_EQ(
793 2, (*(get_perf_context()->level_to_perf_context))[7].bloom_filter_useful);
794 ASSERT_EQ(1, (*(get_perf_context()->level_to_perf_context))[0]
795 .bloom_filter_full_positive);
796 ASSERT_EQ(1, (*(get_perf_context()->level_to_perf_context))[2]
797 .bloom_filter_full_true_positive);
798 ASSERT_EQ(1, (*(get_perf_context()->level_to_perf_context))[0]
799 .block_cache_hit_count);
800 ASSERT_EQ(5, (*(get_perf_context()->level_to_perf_context))[2]
801 .block_cache_hit_count);
802 ASSERT_EQ(2, (*(get_perf_context()->level_to_perf_context))[3]
803 .block_cache_miss_count);
804 ASSERT_EQ(4, (*(get_perf_context()->level_to_perf_context))[1]
805 .block_cache_miss_count);
806 std::string zero_excluded = get_perf_context()->ToString(true);
807 ASSERT_NE(std::string::npos,
808 zero_excluded.find("bloom_filter_useful = 1@level5, 2@level7"));
809 ASSERT_NE(std::string::npos,
810 zero_excluded.find("bloom_filter_full_positive = 1@level0"));
811 ASSERT_NE(std::string::npos,
812 zero_excluded.find("bloom_filter_full_true_positive = 1@level2"));
813 ASSERT_NE(std::string::npos,
814 zero_excluded.find("block_cache_hit_count = 1@level0, 5@level2"));
815 ASSERT_NE(std::string::npos,
816 zero_excluded.find("block_cache_miss_count = 4@level1, 2@level3"));
817}
818
819TEST_F(PerfContextTest, CPUTimer) {
820 DestroyDB(kDbName, Options());
821 auto db = OpenDb();
822 WriteOptions write_options;
823 ReadOptions read_options;
824 SetPerfLevel(PerfLevel::kEnableTimeAndCPUTimeExceptForMutex);
825
826 std::string max_str = "0";
827 for (int i = 0; i < FLAGS_total_keys; ++i) {
828 std::string i_str = ToString(i);
829 std::string key = "k" + i_str;
830 std::string value = "v" + i_str;
831 max_str = max_str > i_str ? max_str : i_str;
832
833 db->Put(write_options, key, value);
834 }
835 std::string last_key = "k" + max_str;
836 std::string last_value = "v" + max_str;
837
838 {
839 // Get
840 get_perf_context()->Reset();
841 std::string value;
842 ASSERT_OK(db->Get(read_options, "k0", &value));
843 ASSERT_EQ(value, "v0");
844
845 if (FLAGS_verbose) {
846 std::cout << "Get CPU time nanos: " << get_perf_context()->get_cpu_nanos
847 << "ns\n";
848 }
849
850 // Iter
851 std::unique_ptr<Iterator> iter(db->NewIterator(read_options));
852
853 // Seek
854 get_perf_context()->Reset();
855 iter->Seek(last_key);
856 ASSERT_TRUE(iter->Valid());
857 ASSERT_EQ(last_value, iter->value().ToString());
858
859 if (FLAGS_verbose) {
860 std::cout << "Iter Seek CPU time nanos: "
861 << get_perf_context()->iter_seek_cpu_nanos << "ns\n";
862 }
863
864 // SeekForPrev
865 get_perf_context()->Reset();
866 iter->SeekForPrev(last_key);
867 ASSERT_TRUE(iter->Valid());
868
869 if (FLAGS_verbose) {
870 std::cout << "Iter SeekForPrev CPU time nanos: "
871 << get_perf_context()->iter_seek_cpu_nanos << "ns\n";
872 }
873
874 // SeekToLast
875 get_perf_context()->Reset();
876 iter->SeekToLast();
877 ASSERT_TRUE(iter->Valid());
878 ASSERT_EQ(last_value, iter->value().ToString());
879
880 if (FLAGS_verbose) {
881 std::cout << "Iter SeekToLast CPU time nanos: "
882 << get_perf_context()->iter_seek_cpu_nanos << "ns\n";
883 }
884
885 // SeekToFirst
886 get_perf_context()->Reset();
887 iter->SeekToFirst();
888 ASSERT_TRUE(iter->Valid());
889 ASSERT_EQ("v0", iter->value().ToString());
890
891 if (FLAGS_verbose) {
892 std::cout << "Iter SeekToFirst CPU time nanos: "
893 << get_perf_context()->iter_seek_cpu_nanos << "ns\n";
894 }
895
896 // Next
897 get_perf_context()->Reset();
898 iter->Next();
899 ASSERT_TRUE(iter->Valid());
900 ASSERT_EQ("v1", iter->value().ToString());
901
902 if (FLAGS_verbose) {
903 std::cout << "Iter Next CPU time nanos: "
904 << get_perf_context()->iter_next_cpu_nanos << "ns\n";
905 }
906
907 // Prev
908 get_perf_context()->Reset();
909 iter->Prev();
910 ASSERT_TRUE(iter->Valid());
911 ASSERT_EQ("v0", iter->value().ToString());
912
913 if (FLAGS_verbose) {
914 std::cout << "Iter Prev CPU time nanos: "
915 << get_perf_context()->iter_prev_cpu_nanos << "ns\n";
916 }
917
918 // monotonically increasing
919 get_perf_context()->Reset();
920 auto count = get_perf_context()->iter_seek_cpu_nanos;
921 for (int i = 0; i < FLAGS_total_keys; ++i) {
922 iter->Seek("k" + ToString(i));
923 ASSERT_TRUE(iter->Valid());
924 ASSERT_EQ("v" + ToString(i), iter->value().ToString());
925 auto next_count = get_perf_context()->iter_seek_cpu_nanos;
926 ASSERT_GT(next_count, count);
927 count = next_count;
928 }
929
930 // iterator creation/destruction; multiple iterators
931 {
932 std::unique_ptr<Iterator> iter2(db->NewIterator(read_options));
933 ASSERT_EQ(count, get_perf_context()->iter_seek_cpu_nanos);
934 iter2->Seek(last_key);
935 ASSERT_TRUE(iter2->Valid());
936 ASSERT_EQ(last_value, iter2->value().ToString());
937 ASSERT_GT(get_perf_context()->iter_seek_cpu_nanos, count);
938 count = get_perf_context()->iter_seek_cpu_nanos;
939 }
940 ASSERT_EQ(count, get_perf_context()->iter_seek_cpu_nanos);
941 }
7c673cae 942}
f67539c2 943} // namespace ROCKSDB_NAMESPACE
7c673cae
FG
944
945int main(int argc, char** argv) {
946 ::testing::InitGoogleTest(&argc, argv);
947
948 for (int i = 1; i < argc; i++) {
949 int n;
950 char junk;
951
952 if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) {
953 FLAGS_write_buffer_size = n;
954 }
955
956 if (sscanf(argv[i], "--total_keys=%d%c", &n, &junk) == 1) {
957 FLAGS_total_keys = n;
958 }
959
960 if (sscanf(argv[i], "--random_key=%d%c", &n, &junk) == 1 &&
961 (n == 0 || n == 1)) {
962 FLAGS_random_key = n;
963 }
964
965 if (sscanf(argv[i], "--use_set_based_memetable=%d%c", &n, &junk) == 1 &&
966 (n == 0 || n == 1)) {
967 FLAGS_use_set_based_memetable = n;
968 }
969
970 if (sscanf(argv[i], "--verbose=%d%c", &n, &junk) == 1 &&
971 (n == 0 || n == 1)) {
972 FLAGS_verbose = n;
973 }
974 }
975
976 if (FLAGS_verbose) {
977 std::cout << kDbName << "\n";
978 }
979
980 return RUN_ALL_TESTS();
981}