| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981 | //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.//  This source code is licensed under both the GPLv2 (found in the//  COPYING file in the root directory) and Apache 2.0 License//  (found in the LICENSE.Apache file in the root directory).//#include <algorithm>#include <iostream>#include <thread>#include <vector>#include "monitoring/histogram.h"#include "monitoring/instrumented_mutex.h"#include "monitoring/perf_context_imp.h"#include "monitoring/thread_status_util.h"#include "port/port.h"#include "rocksdb/db.h"#include "rocksdb/memtablerep.h"#include "rocksdb/perf_context.h"#include "rocksdb/slice_transform.h"#include "test_util/testharness.h"#include "util/stop_watch.h"#include "util/string_util.h"#include "utilities/merge_operators.h"bool FLAGS_random_key = false;bool FLAGS_use_set_based_memetable = false;int FLAGS_total_keys = 100;int FLAGS_write_buffer_size = 1000000000;int FLAGS_max_write_buffer_number = 8;int FLAGS_min_write_buffer_number_to_merge = 7;bool FLAGS_verbose = false;// Path to the database on file systemconst std::string kDbName =    ROCKSDB_NAMESPACE::test::PerThreadDBPath("perf_context_test");namespace ROCKSDB_NAMESPACE {std::shared_ptr<DB> OpenDb(bool read_only = false) {    DB* db;    Options options;    options.create_if_missing = true;    options.max_open_files = -1;    options.write_buffer_size = FLAGS_write_buffer_size;    options.max_write_buffer_number = FLAGS_max_write_buffer_number;    options.min_write_buffer_number_to_merge =      FLAGS_min_write_buffer_number_to_merge;    if (FLAGS_use_set_based_memetable) {#ifndef ROCKSDB_LITE      options.prefix_extractor.reset(          ROCKSDB_NAMESPACE::NewFixedPrefixTransform(0));      options.memtable_factory.reset(NewHashSkipListRepFactory());#endif  // ROCKSDB_LITE    }    Status s;    if (!read_only) {      s = DB::Open(options, kDbName, &db);    } else {      s = DB::OpenForReadOnly(options, kDbName, &db);    }    EXPECT_OK(s);    return std::shared_ptr<DB>(db);}class PerfContextTest : public testing::Test {};TEST_F(PerfContextTest, SeekIntoDeletion) {  DestroyDB(kDbName, Options());  auto db = OpenDb();  WriteOptions write_options;  ReadOptions read_options;  for (int i = 0; i < FLAGS_total_keys; ++i) {    std::string key = "k" + ToString(i);    std::string value = "v" + ToString(i);    db->Put(write_options, key, value);  }  for (int i = 0; i < FLAGS_total_keys -1 ; ++i) {    std::string key = "k" + ToString(i);    db->Delete(write_options, key);  }  HistogramImpl hist_get;  HistogramImpl hist_get_time;  for (int i = 0; i < FLAGS_total_keys - 1; ++i) {    std::string key = "k" + ToString(i);    std::string value;    get_perf_context()->Reset();    StopWatchNano timer(Env::Default());    timer.Start();    auto status = db->Get(read_options, key, &value);    auto elapsed_nanos = timer.ElapsedNanos();    ASSERT_TRUE(status.IsNotFound());    hist_get.Add(get_perf_context()->user_key_comparison_count);    hist_get_time.Add(elapsed_nanos);  }  if (FLAGS_verbose) {    std::cout << "Get user key comparison: \n" << hist_get.ToString()              << "Get time: \n" << hist_get_time.ToString();  }  {    HistogramImpl hist_seek_to_first;    std::unique_ptr<Iterator> iter(db->NewIterator(read_options));    get_perf_context()->Reset();    StopWatchNano timer(Env::Default(), true);    iter->SeekToFirst();    hist_seek_to_first.Add(get_perf_context()->user_key_comparison_count);    auto elapsed_nanos = timer.ElapsedNanos();    if (FLAGS_verbose) {      std::cout << "SeekToFirst uesr key comparison: \n"                << hist_seek_to_first.ToString()                << "ikey skipped: " << get_perf_context()->internal_key_skipped_count                << "\n"                << "idelete skipped: "                << get_perf_context()->internal_delete_skipped_count << "\n"                << "elapsed: " << elapsed_nanos << "\n";    }  }  HistogramImpl hist_seek;  for (int i = 0; i < FLAGS_total_keys; ++i) {    std::unique_ptr<Iterator> iter(db->NewIterator(read_options));    std::string key = "k" + ToString(i);    get_perf_context()->Reset();    StopWatchNano timer(Env::Default(), true);    iter->Seek(key);    auto elapsed_nanos = timer.ElapsedNanos();    hist_seek.Add(get_perf_context()->user_key_comparison_count);    if (FLAGS_verbose) {      std::cout << "seek cmp: " << get_perf_context()->user_key_comparison_count                << " ikey skipped " << get_perf_context()->internal_key_skipped_count                << " idelete skipped "                << get_perf_context()->internal_delete_skipped_count                << " elapsed: " << elapsed_nanos << "ns\n";    }    get_perf_context()->Reset();    ASSERT_TRUE(iter->Valid());    StopWatchNano timer2(Env::Default(), true);    iter->Next();    auto elapsed_nanos2 = timer2.ElapsedNanos();    if (FLAGS_verbose) {      std::cout << "next cmp: " << get_perf_context()->user_key_comparison_count                << "elapsed: " << elapsed_nanos2 << "ns\n";    }  }  if (FLAGS_verbose) {    std::cout << "Seek uesr key comparison: \n" << hist_seek.ToString();  }}TEST_F(PerfContextTest, StopWatchNanoOverhead) {  // profile the timer cost by itself!  const int kTotalIterations = 1000000;  std::vector<uint64_t> timings(kTotalIterations);  StopWatchNano timer(Env::Default(), true);  for (auto& timing : timings) {    timing = timer.ElapsedNanos(true /* reset */);  }  HistogramImpl histogram;  for (const auto timing : timings) {    histogram.Add(timing);  }  if (FLAGS_verbose) {    std::cout << histogram.ToString();  }}TEST_F(PerfContextTest, StopWatchOverhead) {  // profile the timer cost by itself!  const int kTotalIterations = 1000000;  uint64_t elapsed = 0;  std::vector<uint64_t> timings(kTotalIterations);  StopWatch timer(Env::Default(), nullptr, 0, &elapsed);  for (auto& timing : timings) {    timing = elapsed;  }  HistogramImpl histogram;  uint64_t prev_timing = 0;  for (const auto timing : timings) {    histogram.Add(timing - prev_timing);    prev_timing = timing;  }  if (FLAGS_verbose) {    std::cout << histogram.ToString();  }}void ProfileQueries(bool enabled_time = false) {  DestroyDB(kDbName, Options());    // Start this test with a fresh DB  auto db = OpenDb();  WriteOptions write_options;  ReadOptions read_options;  HistogramImpl hist_put;  HistogramImpl hist_get;  HistogramImpl hist_get_snapshot;  HistogramImpl hist_get_memtable;  HistogramImpl hist_get_files;  HistogramImpl hist_get_post_process;  HistogramImpl hist_num_memtable_checked;  HistogramImpl hist_mget;  HistogramImpl hist_mget_snapshot;  HistogramImpl hist_mget_memtable;  HistogramImpl hist_mget_files;  HistogramImpl hist_mget_post_process;  HistogramImpl hist_mget_num_memtable_checked;  HistogramImpl hist_write_pre_post;  HistogramImpl hist_write_wal_time;  HistogramImpl hist_write_memtable_time;  HistogramImpl hist_write_delay_time;  HistogramImpl hist_write_thread_wait_nanos;  HistogramImpl hist_write_scheduling_time;  uint64_t total_db_mutex_nanos = 0;  if (FLAGS_verbose) {    std::cout << "Inserting " << FLAGS_total_keys << " key/value pairs\n...\n";  }  std::vector<int> keys;  const int kFlushFlag = -1;  for (int i = 0; i < FLAGS_total_keys; ++i) {    keys.push_back(i);    if (i == FLAGS_total_keys / 2) {      // Issuing a flush in the middle.      keys.push_back(kFlushFlag);    }  }  if (FLAGS_random_key) {    std::random_shuffle(keys.begin(), keys.end());  }#ifndef NDEBUG  ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 1U);#endif  int num_mutex_waited = 0;  for (const int i : keys) {    if (i == kFlushFlag) {      FlushOptions fo;      db->Flush(fo);      continue;    }    std::string key = "k" + ToString(i);    std::string value = "v" + ToString(i);    std::vector<std::string> values;    get_perf_context()->Reset();    db->Put(write_options, key, value);    if (++num_mutex_waited > 3) {#ifndef NDEBUG      ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0U);#endif    }    hist_write_pre_post.Add(        get_perf_context()->write_pre_and_post_process_time);    hist_write_wal_time.Add(get_perf_context()->write_wal_time);    hist_write_memtable_time.Add(get_perf_context()->write_memtable_time);    hist_write_delay_time.Add(get_perf_context()->write_delay_time);    hist_write_thread_wait_nanos.Add(        get_perf_context()->write_thread_wait_nanos);    hist_write_scheduling_time.Add(        get_perf_context()->write_scheduling_flushes_compactions_time);    hist_put.Add(get_perf_context()->user_key_comparison_count);    total_db_mutex_nanos += get_perf_context()->db_mutex_lock_nanos;  }#ifndef NDEBUG  ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0U);#endif  for (const int i : keys) {    if (i == kFlushFlag) {      continue;    }    std::string key = "k" + ToString(i);    std::string expected_value = "v" + ToString(i);    std::string value;    std::vector<Slice> multiget_keys = {Slice(key)};    std::vector<std::string> values;    get_perf_context()->Reset();    ASSERT_OK(db->Get(read_options, key, &value));    ASSERT_EQ(expected_value, value);    hist_get_snapshot.Add(get_perf_context()->get_snapshot_time);    hist_get_memtable.Add(get_perf_context()->get_from_memtable_time);    hist_get_files.Add(get_perf_context()->get_from_output_files_time);    hist_num_memtable_checked.Add(get_perf_context()->get_from_memtable_count);    hist_get_post_process.Add(get_perf_context()->get_post_process_time);    hist_get.Add(get_perf_context()->user_key_comparison_count);    get_perf_context()->Reset();    db->MultiGet(read_options, multiget_keys, &values);    hist_mget_snapshot.Add(get_perf_context()->get_snapshot_time);    hist_mget_memtable.Add(get_perf_context()->get_from_memtable_time);    hist_mget_files.Add(get_perf_context()->get_from_output_files_time);    hist_mget_num_memtable_checked.Add(get_perf_context()->get_from_memtable_count);    hist_mget_post_process.Add(get_perf_context()->get_post_process_time);    hist_mget.Add(get_perf_context()->user_key_comparison_count);  }  if (FLAGS_verbose) {    std::cout << "Put uesr key comparison: \n" << hist_put.ToString()              << "Get uesr key comparison: \n" << hist_get.ToString()              << "MultiGet uesr key comparison: \n" << hist_get.ToString();    std::cout << "Put(): Pre and Post Process Time: \n"              << hist_write_pre_post.ToString() << " Writing WAL time: \n"              << hist_write_wal_time.ToString() << "\n"              << " Writing Mem Table time: \n"              << hist_write_memtable_time.ToString() << "\n"              << " Write Delay: \n" << hist_write_delay_time.ToString() << "\n"              << " Waiting for Batch time: \n"              << hist_write_thread_wait_nanos.ToString() << "\n"              << " Scheduling Flushes and Compactions Time: \n"              << hist_write_scheduling_time.ToString() << "\n"              << " Total DB mutex nanos: \n" << total_db_mutex_nanos << "\n";    std::cout << "Get(): Time to get snapshot: \n"              << hist_get_snapshot.ToString()              << " Time to get value from memtables: \n"              << hist_get_memtable.ToString() << "\n"              << " Time to get value from output files: \n"              << hist_get_files.ToString() << "\n"              << " Number of memtables checked: \n"              << hist_num_memtable_checked.ToString() << "\n"              << " Time to post process: \n" << hist_get_post_process.ToString()              << "\n";    std::cout << "MultiGet(): Time to get snapshot: \n"              << hist_mget_snapshot.ToString()              << " Time to get value from memtables: \n"              << hist_mget_memtable.ToString() << "\n"              << " Time to get value from output files: \n"              << hist_mget_files.ToString() << "\n"              << " Number of memtables checked: \n"              << hist_mget_num_memtable_checked.ToString() << "\n"              << " Time to post process: \n"              << hist_mget_post_process.ToString() << "\n";  }  if (enabled_time) {    ASSERT_GT(hist_get.Average(), 0);    ASSERT_GT(hist_get_snapshot.Average(), 0);    ASSERT_GT(hist_get_memtable.Average(), 0);    ASSERT_GT(hist_get_files.Average(), 0);    ASSERT_GT(hist_get_post_process.Average(), 0);    ASSERT_GT(hist_num_memtable_checked.Average(), 0);    ASSERT_GT(hist_mget.Average(), 0);    ASSERT_GT(hist_mget_snapshot.Average(), 0);    ASSERT_GT(hist_mget_memtable.Average(), 0);    ASSERT_GT(hist_mget_files.Average(), 0);    ASSERT_GT(hist_mget_post_process.Average(), 0);    ASSERT_GT(hist_mget_num_memtable_checked.Average(), 0);    EXPECT_GT(hist_write_pre_post.Average(), 0);    EXPECT_GT(hist_write_wal_time.Average(), 0);    EXPECT_GT(hist_write_memtable_time.Average(), 0);    EXPECT_EQ(hist_write_delay_time.Average(), 0);    EXPECT_EQ(hist_write_thread_wait_nanos.Average(), 0);    EXPECT_GT(hist_write_scheduling_time.Average(), 0);#ifndef NDEBUG    ASSERT_GT(total_db_mutex_nanos, 2000U);#endif  }  db.reset();  db = OpenDb(true);  hist_get.Clear();  hist_get_snapshot.Clear();  hist_get_memtable.Clear();  hist_get_files.Clear();  hist_get_post_process.Clear();  hist_num_memtable_checked.Clear();  hist_mget.Clear();  hist_mget_snapshot.Clear();  hist_mget_memtable.Clear();  hist_mget_files.Clear();  hist_mget_post_process.Clear();  hist_mget_num_memtable_checked.Clear();  for (const int i : keys) {    if (i == kFlushFlag) {      continue;    }    std::string key = "k" + ToString(i);    std::string expected_value = "v" + ToString(i);    std::string value;    std::vector<Slice> multiget_keys = {Slice(key)};    std::vector<std::string> values;    get_perf_context()->Reset();    ASSERT_OK(db->Get(read_options, key, &value));    ASSERT_EQ(expected_value, value);    hist_get_snapshot.Add(get_perf_context()->get_snapshot_time);    hist_get_memtable.Add(get_perf_context()->get_from_memtable_time);    hist_get_files.Add(get_perf_context()->get_from_output_files_time);    hist_num_memtable_checked.Add(get_perf_context()->get_from_memtable_count);    hist_get_post_process.Add(get_perf_context()->get_post_process_time);    hist_get.Add(get_perf_context()->user_key_comparison_count);    get_perf_context()->Reset();    db->MultiGet(read_options, multiget_keys, &values);    hist_mget_snapshot.Add(get_perf_context()->get_snapshot_time);    hist_mget_memtable.Add(get_perf_context()->get_from_memtable_time);    hist_mget_files.Add(get_perf_context()->get_from_output_files_time);    hist_mget_num_memtable_checked.Add(get_perf_context()->get_from_memtable_count);    hist_mget_post_process.Add(get_perf_context()->get_post_process_time);    hist_mget.Add(get_perf_context()->user_key_comparison_count);  }  if (FLAGS_verbose) {    std::cout << "ReadOnly Get uesr key comparison: \n" << hist_get.ToString()              << "ReadOnly MultiGet uesr key comparison: \n"              << hist_mget.ToString();    std::cout << "ReadOnly Get(): Time to get snapshot: \n"              << hist_get_snapshot.ToString()              << " Time to get value from memtables: \n"              << hist_get_memtable.ToString() << "\n"              << " Time to get value from output files: \n"              << hist_get_files.ToString() << "\n"              << " Number of memtables checked: \n"              << hist_num_memtable_checked.ToString() << "\n"              << " Time to post process: \n" << hist_get_post_process.ToString()              << "\n";    std::cout << "ReadOnly MultiGet(): Time to get snapshot: \n"              << hist_mget_snapshot.ToString()              << " Time to get value from memtables: \n"              << hist_mget_memtable.ToString() << "\n"              << " Time to get value from output files: \n"              << hist_mget_files.ToString() << "\n"              << " Number of memtables checked: \n"              << hist_mget_num_memtable_checked.ToString() << "\n"              << " Time to post process: \n"              << hist_mget_post_process.ToString() << "\n";  }  if (enabled_time) {    ASSERT_GT(hist_get.Average(), 0);    ASSERT_GT(hist_get_memtable.Average(), 0);    ASSERT_GT(hist_get_files.Average(), 0);    ASSERT_GT(hist_num_memtable_checked.Average(), 0);    // In read-only mode Get(), no super version operation is needed    ASSERT_EQ(hist_get_post_process.Average(), 0);    ASSERT_GT(hist_get_snapshot.Average(), 0);    ASSERT_GT(hist_mget.Average(), 0);    ASSERT_GT(hist_mget_snapshot.Average(), 0);    ASSERT_GT(hist_mget_memtable.Average(), 0);    ASSERT_GT(hist_mget_files.Average(), 0);    ASSERT_GT(hist_mget_post_process.Average(), 0);    ASSERT_GT(hist_mget_num_memtable_checked.Average(), 0);  }}#ifndef ROCKSDB_LITETEST_F(PerfContextTest, KeyComparisonCount) {  SetPerfLevel(kEnableCount);  ProfileQueries();  SetPerfLevel(kDisable);  ProfileQueries();  SetPerfLevel(kEnableTime);  ProfileQueries(true);}#endif  // ROCKSDB_LITE// make perf_context_test// export ROCKSDB_TESTS=PerfContextTest.SeekKeyComparison// For one memtable:// ./perf_context_test --write_buffer_size=500000 --total_keys=10000// For two memtables:// ./perf_context_test --write_buffer_size=250000 --total_keys=10000// Specify --random_key=1 to shuffle the key before insertion// Results show that, for sequential insertion, worst-case Seek Key comparison// is close to the total number of keys (linear), when there is only one// memtable. When there are two memtables, even the avg Seek Key comparison// starts to become linear to the input size.TEST_F(PerfContextTest, SeekKeyComparison) {  DestroyDB(kDbName, Options());  auto db = OpenDb();  WriteOptions write_options;  ReadOptions read_options;  if (FLAGS_verbose) {    std::cout << "Inserting " << FLAGS_total_keys << " key/value pairs\n...\n";  }  std::vector<int> keys;  for (int i = 0; i < FLAGS_total_keys; ++i) {    keys.push_back(i);  }  if (FLAGS_random_key) {    std::random_shuffle(keys.begin(), keys.end());  }  HistogramImpl hist_put_time;  HistogramImpl hist_wal_time;  HistogramImpl hist_time_diff;  SetPerfLevel(kEnableTime);  StopWatchNano timer(Env::Default());  for (const int i : keys) {    std::string key = "k" + ToString(i);    std::string value = "v" + ToString(i);    get_perf_context()->Reset();    timer.Start();    db->Put(write_options, key, value);    auto put_time = timer.ElapsedNanos();    hist_put_time.Add(put_time);    hist_wal_time.Add(get_perf_context()->write_wal_time);    hist_time_diff.Add(put_time - get_perf_context()->write_wal_time);  }  if (FLAGS_verbose) {    std::cout << "Put time:\n" << hist_put_time.ToString() << "WAL time:\n"              << hist_wal_time.ToString() << "time diff:\n"              << hist_time_diff.ToString();  }  HistogramImpl hist_seek;  HistogramImpl hist_next;  for (int i = 0; i < FLAGS_total_keys; ++i) {    std::string key = "k" + ToString(i);    std::string value = "v" + ToString(i);    std::unique_ptr<Iterator> iter(db->NewIterator(read_options));    get_perf_context()->Reset();    iter->Seek(key);    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->value().ToString(), value);    hist_seek.Add(get_perf_context()->user_key_comparison_count);  }  std::unique_ptr<Iterator> iter(db->NewIterator(read_options));  for (iter->SeekToFirst(); iter->Valid();) {    get_perf_context()->Reset();    iter->Next();    hist_next.Add(get_perf_context()->user_key_comparison_count);  }  if (FLAGS_verbose) {    std::cout << "Seek:\n" << hist_seek.ToString() << "Next:\n"              << hist_next.ToString();  }}TEST_F(PerfContextTest, DBMutexLockCounter) {  int stats_code[] = {0, static_cast<int>(DB_MUTEX_WAIT_MICROS)};  for (PerfLevel perf_level_test :       {PerfLevel::kEnableTimeExceptForMutex, PerfLevel::kEnableTime}) {    for (int c = 0; c < 2; ++c) {    InstrumentedMutex mutex(nullptr, Env::Default(), stats_code[c]);    mutex.Lock();    ROCKSDB_NAMESPACE::port::Thread child_thread([&] {      SetPerfLevel(perf_level_test);      get_perf_context()->Reset();      ASSERT_EQ(get_perf_context()->db_mutex_lock_nanos, 0);      mutex.Lock();      mutex.Unlock();      if (perf_level_test == PerfLevel::kEnableTimeExceptForMutex ||          stats_code[c] != DB_MUTEX_WAIT_MICROS) {        ASSERT_EQ(get_perf_context()->db_mutex_lock_nanos, 0);      } else {        // increment the counter only when it's a DB Mutex        ASSERT_GT(get_perf_context()->db_mutex_lock_nanos, 0);      }    });    Env::Default()->SleepForMicroseconds(100);    mutex.Unlock();    child_thread.join();  }  }}TEST_F(PerfContextTest, FalseDBMutexWait) {  SetPerfLevel(kEnableTime);  int stats_code[] = {0, static_cast<int>(DB_MUTEX_WAIT_MICROS)};  for (int c = 0; c < 2; ++c) {    InstrumentedMutex mutex(nullptr, Env::Default(), stats_code[c]);    InstrumentedCondVar lock(&mutex);    get_perf_context()->Reset();    mutex.Lock();    lock.TimedWait(100);    mutex.Unlock();    if (stats_code[c] == static_cast<int>(DB_MUTEX_WAIT_MICROS)) {      // increment the counter only when it's a DB Mutex      ASSERT_GT(get_perf_context()->db_condition_wait_nanos, 0);    } else {      ASSERT_EQ(get_perf_context()->db_condition_wait_nanos, 0);    }  }}TEST_F(PerfContextTest, ToString) {  get_perf_context()->Reset();  get_perf_context()->block_read_count = 12345;  std::string zero_included = get_perf_context()->ToString();  ASSERT_NE(std::string::npos, zero_included.find("= 0"));  ASSERT_NE(std::string::npos, zero_included.find("= 12345"));  std::string zero_excluded = get_perf_context()->ToString(true);  ASSERT_EQ(std::string::npos, zero_excluded.find("= 0"));  ASSERT_NE(std::string::npos, zero_excluded.find("= 12345"));}TEST_F(PerfContextTest, MergeOperatorTime) {  DestroyDB(kDbName, Options());  DB* db;  Options options;  options.create_if_missing = true;  options.merge_operator = MergeOperators::CreateStringAppendOperator();  Status s = DB::Open(options, kDbName, &db);  EXPECT_OK(s);  std::string val;  ASSERT_OK(db->Merge(WriteOptions(), "k1", "val1"));  ASSERT_OK(db->Merge(WriteOptions(), "k1", "val2"));  ASSERT_OK(db->Merge(WriteOptions(), "k1", "val3"));  ASSERT_OK(db->Merge(WriteOptions(), "k1", "val4"));  SetPerfLevel(kEnableTime);  get_perf_context()->Reset();  ASSERT_OK(db->Get(ReadOptions(), "k1", &val));#ifdef OS_SOLARIS  for (int i = 0; i < 100; i++) {    ASSERT_OK(db->Get(ReadOptions(), "k1", &val));  }#endif  EXPECT_GT(get_perf_context()->merge_operator_time_nanos, 0);  ASSERT_OK(db->Flush(FlushOptions()));  get_perf_context()->Reset();  ASSERT_OK(db->Get(ReadOptions(), "k1", &val));#ifdef OS_SOLARIS  for (int i = 0; i < 100; i++) {    ASSERT_OK(db->Get(ReadOptions(), "k1", &val));  }#endif  EXPECT_GT(get_perf_context()->merge_operator_time_nanos, 0);  ASSERT_OK(db->CompactRange(CompactRangeOptions(), nullptr, nullptr));  get_perf_context()->Reset();  ASSERT_OK(db->Get(ReadOptions(), "k1", &val));#ifdef OS_SOLARIS  for (int i = 0; i < 100; i++) {    ASSERT_OK(db->Get(ReadOptions(), "k1", &val));  }#endif  EXPECT_GT(get_perf_context()->merge_operator_time_nanos, 0);  delete db;}TEST_F(PerfContextTest, CopyAndMove) {  // Assignment operator  {    get_perf_context()->Reset();    get_perf_context()->EnablePerLevelPerfContext();    PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 5);    ASSERT_EQ(        1,        (*(get_perf_context()->level_to_perf_context))[5].bloom_filter_useful);    PerfContext perf_context_assign;    perf_context_assign = *get_perf_context();    ASSERT_EQ(        1,        (*(perf_context_assign.level_to_perf_context))[5].bloom_filter_useful);    get_perf_context()->ClearPerLevelPerfContext();    get_perf_context()->Reset();    ASSERT_EQ(        1,        (*(perf_context_assign.level_to_perf_context))[5].bloom_filter_useful);    perf_context_assign.ClearPerLevelPerfContext();    perf_context_assign.Reset();  }  // Copy constructor  {    get_perf_context()->Reset();    get_perf_context()->EnablePerLevelPerfContext();    PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 5);    ASSERT_EQ(        1,        (*(get_perf_context()->level_to_perf_context))[5].bloom_filter_useful);    PerfContext perf_context_copy(*get_perf_context());    ASSERT_EQ(        1, (*(perf_context_copy.level_to_perf_context))[5].bloom_filter_useful);    get_perf_context()->ClearPerLevelPerfContext();    get_perf_context()->Reset();    ASSERT_EQ(        1, (*(perf_context_copy.level_to_perf_context))[5].bloom_filter_useful);    perf_context_copy.ClearPerLevelPerfContext();    perf_context_copy.Reset();  }  // Move constructor  {    get_perf_context()->Reset();    get_perf_context()->EnablePerLevelPerfContext();    PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 5);    ASSERT_EQ(        1,        (*(get_perf_context()->level_to_perf_context))[5].bloom_filter_useful);    PerfContext perf_context_move = std::move(*get_perf_context());    ASSERT_EQ(        1, (*(perf_context_move.level_to_perf_context))[5].bloom_filter_useful);    get_perf_context()->ClearPerLevelPerfContext();    get_perf_context()->Reset();    ASSERT_EQ(        1, (*(perf_context_move.level_to_perf_context))[5].bloom_filter_useful);    perf_context_move.ClearPerLevelPerfContext();    perf_context_move.Reset();  }}TEST_F(PerfContextTest, PerfContextDisableEnable) {  get_perf_context()->Reset();  get_perf_context()->EnablePerLevelPerfContext();  PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_full_positive, 1, 0);  get_perf_context()->DisablePerLevelPerfContext();  PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 5);  get_perf_context()->EnablePerLevelPerfContext();  PERF_COUNTER_BY_LEVEL_ADD(block_cache_hit_count, 1, 0);  get_perf_context()->DisablePerLevelPerfContext();  PerfContext perf_context_copy(*get_perf_context());  ASSERT_EQ(1, (*(perf_context_copy.level_to_perf_context))[0]                   .bloom_filter_full_positive);  // this was set when per level perf context is disabled, should not be copied  ASSERT_NE(      1, (*(perf_context_copy.level_to_perf_context))[5].bloom_filter_useful);  ASSERT_EQ(      1, (*(perf_context_copy.level_to_perf_context))[0].block_cache_hit_count);  perf_context_copy.ClearPerLevelPerfContext();  perf_context_copy.Reset();  get_perf_context()->ClearPerLevelPerfContext();  get_perf_context()->Reset();}TEST_F(PerfContextTest, PerfContextByLevelGetSet) {  get_perf_context()->Reset();  get_perf_context()->EnablePerLevelPerfContext();  PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_full_positive, 1, 0);  PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 5);  PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 7);  PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 7);  PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_full_true_positive, 1, 2);  PERF_COUNTER_BY_LEVEL_ADD(block_cache_hit_count, 1, 0);  PERF_COUNTER_BY_LEVEL_ADD(block_cache_hit_count, 5, 2);  PERF_COUNTER_BY_LEVEL_ADD(block_cache_miss_count, 2, 3);  PERF_COUNTER_BY_LEVEL_ADD(block_cache_miss_count, 4, 1);  ASSERT_EQ(      0, (*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);  ASSERT_EQ(      1, (*(get_perf_context()->level_to_perf_context))[5].bloom_filter_useful);  ASSERT_EQ(      2, (*(get_perf_context()->level_to_perf_context))[7].bloom_filter_useful);  ASSERT_EQ(1, (*(get_perf_context()->level_to_perf_context))[0]                   .bloom_filter_full_positive);  ASSERT_EQ(1, (*(get_perf_context()->level_to_perf_context))[2]                   .bloom_filter_full_true_positive);  ASSERT_EQ(1, (*(get_perf_context()->level_to_perf_context))[0]                  .block_cache_hit_count);  ASSERT_EQ(5, (*(get_perf_context()->level_to_perf_context))[2]                  .block_cache_hit_count);  ASSERT_EQ(2, (*(get_perf_context()->level_to_perf_context))[3]                  .block_cache_miss_count);  ASSERT_EQ(4, (*(get_perf_context()->level_to_perf_context))[1]                  .block_cache_miss_count);  std::string zero_excluded = get_perf_context()->ToString(true);  ASSERT_NE(std::string::npos,            zero_excluded.find("bloom_filter_useful = 1@level5, 2@level7"));  ASSERT_NE(std::string::npos,            zero_excluded.find("bloom_filter_full_positive = 1@level0"));  ASSERT_NE(std::string::npos,            zero_excluded.find("bloom_filter_full_true_positive = 1@level2"));  ASSERT_NE(std::string::npos,            zero_excluded.find("block_cache_hit_count = 1@level0, 5@level2"));  ASSERT_NE(std::string::npos,            zero_excluded.find("block_cache_miss_count = 4@level1, 2@level3"));}TEST_F(PerfContextTest, CPUTimer) {  DestroyDB(kDbName, Options());  auto db = OpenDb();  WriteOptions write_options;  ReadOptions read_options;  SetPerfLevel(PerfLevel::kEnableTimeAndCPUTimeExceptForMutex);  std::string max_str = "0";  for (int i = 0; i < FLAGS_total_keys; ++i) {    std::string i_str = ToString(i);    std::string key = "k" + i_str;    std::string value = "v" + i_str;    max_str = max_str > i_str ? max_str : i_str;    db->Put(write_options, key, value);  }  std::string last_key = "k" + max_str;  std::string last_value = "v" + max_str;  {    // Get    get_perf_context()->Reset();    std::string value;    ASSERT_OK(db->Get(read_options, "k0", &value));    ASSERT_EQ(value, "v0");    if (FLAGS_verbose) {      std::cout << "Get CPU time nanos: " << get_perf_context()->get_cpu_nanos                << "ns\n";    }    // Iter    std::unique_ptr<Iterator> iter(db->NewIterator(read_options));    // Seek    get_perf_context()->Reset();    iter->Seek(last_key);    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(last_value, iter->value().ToString());    if (FLAGS_verbose) {      std::cout << "Iter Seek CPU time nanos: "                << get_perf_context()->iter_seek_cpu_nanos << "ns\n";    }    // SeekForPrev    get_perf_context()->Reset();    iter->SeekForPrev(last_key);    ASSERT_TRUE(iter->Valid());    if (FLAGS_verbose) {      std::cout << "Iter SeekForPrev CPU time nanos: "                << get_perf_context()->iter_seek_cpu_nanos << "ns\n";    }    // SeekToLast    get_perf_context()->Reset();    iter->SeekToLast();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(last_value, iter->value().ToString());    if (FLAGS_verbose) {      std::cout << "Iter SeekToLast CPU time nanos: "                << get_perf_context()->iter_seek_cpu_nanos << "ns\n";    }    // SeekToFirst    get_perf_context()->Reset();    iter->SeekToFirst();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ("v0", iter->value().ToString());    if (FLAGS_verbose) {      std::cout << "Iter SeekToFirst CPU time nanos: "                << get_perf_context()->iter_seek_cpu_nanos << "ns\n";    }    // Next    get_perf_context()->Reset();    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ("v1", iter->value().ToString());    if (FLAGS_verbose) {      std::cout << "Iter Next CPU time nanos: "                << get_perf_context()->iter_next_cpu_nanos << "ns\n";    }    // Prev    get_perf_context()->Reset();    iter->Prev();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ("v0", iter->value().ToString());    if (FLAGS_verbose) {      std::cout << "Iter Prev CPU time nanos: "                << get_perf_context()->iter_prev_cpu_nanos << "ns\n";    }    // monotonically increasing    get_perf_context()->Reset();    auto count = get_perf_context()->iter_seek_cpu_nanos;    for (int i = 0; i < FLAGS_total_keys; ++i) {      iter->Seek("k" + ToString(i));      ASSERT_TRUE(iter->Valid());      ASSERT_EQ("v" + ToString(i), iter->value().ToString());      auto next_count = get_perf_context()->iter_seek_cpu_nanos;      ASSERT_GT(next_count, count);      count = next_count;    }    // iterator creation/destruction; multiple iterators    {      std::unique_ptr<Iterator> iter2(db->NewIterator(read_options));      ASSERT_EQ(count, get_perf_context()->iter_seek_cpu_nanos);      iter2->Seek(last_key);      ASSERT_TRUE(iter2->Valid());      ASSERT_EQ(last_value, iter2->value().ToString());      ASSERT_GT(get_perf_context()->iter_seek_cpu_nanos, count);      count = get_perf_context()->iter_seek_cpu_nanos;    }    ASSERT_EQ(count, get_perf_context()->iter_seek_cpu_nanos);  }}}  // namespace ROCKSDB_NAMESPACEint main(int argc, char** argv) {  ::testing::InitGoogleTest(&argc, argv);  for (int i = 1; i < argc; i++) {    int n;    char junk;    if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) {      FLAGS_write_buffer_size = n;    }    if (sscanf(argv[i], "--total_keys=%d%c", &n, &junk) == 1) {      FLAGS_total_keys = n;    }    if (sscanf(argv[i], "--random_key=%d%c", &n, &junk) == 1 &&        (n == 0 || n == 1)) {      FLAGS_random_key = n;    }    if (sscanf(argv[i], "--use_set_based_memetable=%d%c", &n, &junk) == 1 &&        (n == 0 || n == 1)) {      FLAGS_use_set_based_memetable = n;    }    if (sscanf(argv[i], "--verbose=%d%c", &n, &junk) == 1 &&        (n == 0 || n == 1)) {      FLAGS_verbose = n;    }  }  if (FLAGS_verbose) {    std::cout << kDbName << "\n";  }  return RUN_ALL_TESTS();}
 |