From d9dd2a19269f22fc350f92fe83fb68eae957f4c7 Mon Sep 17 00:00:00 2001 From: Haobo Xu Date: Mon, 12 Aug 2013 23:59:04 -0700 Subject: [PATCH] [RocksDB] Expose thread local perf counter for low overhead, per call level performance statistics. Summary: As title. No locking/atomic is needed due to thread local. There is also no need to modify the existing client interface, in order to expose related counters. perf_context_test shows a simple example of retrieving the number of user key comparison done for each put and get call. More counters could be added later. Sample output ./perf_context_test 1000000 ==== Test PerfContextTest.KeyComparisonCount Inserting 1000000 key/value pairs ... total user key comparison get: 43446523 total user key comparison put: 8017877 max user key comparison get: 88939 avg user key comparison get:43 Basically, the current skiplist does well on average, but could perform poorly in extreme cases. Test Plan: run perf_context_test Reviewers: dhruba Differential Revision: https://reviews.facebook.net/D12225 --- Makefile | 3 ++ db/dbformat.cc | 2 + db/perf_context.cc | 12 +++++ db/perf_context_test.cc | 81 ++++++++++++++++++++++++++++++++++ include/leveldb/perf_context.h | 24 ++++++++++ 5 files changed, 122 insertions(+) create mode 100644 db/perf_context.cc create mode 100644 db/perf_context_test.cc create mode 100644 include/leveldb/perf_context.h diff --git a/Makefile b/Makefile index 7cda1b148..d89c87f1e 100644 --- a/Makefile +++ b/Makefile @@ -210,6 +210,9 @@ crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS) +perf_context_test: db/perf_context_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) db/perf_context_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) + ttl_test: utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS) diff --git a/db/dbformat.cc b/db/dbformat.cc index ec40bccc3..17288a6a8 100644 --- a/db/dbformat.cc +++ b/db/dbformat.cc @@ -6,6 +6,7 @@ #include "db/dbformat.h" #include "port/port.h" #include "util/coding.h" +#include "include/leveldb/perf_context.h" namespace leveldb { @@ -53,6 +54,7 @@ int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const { // decreasing sequence number // decreasing type (though sequence# should be enough to disambiguate) int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey)); + perf_context.user_key_comparison_count++; if (r == 0) { const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8); const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8); diff --git a/db/perf_context.cc b/db/perf_context.cc new file mode 100644 index 000000000..d3293b4a2 --- /dev/null +++ b/db/perf_context.cc @@ -0,0 +1,12 @@ +#include "include/leveldb/perf_context.h" + + +namespace leveldb { + +void PerfContext::Reset() { + user_key_comparison_count = 0; +} + +__thread PerfContext perf_context; + +} diff --git a/db/perf_context_test.cc b/db/perf_context_test.cc new file mode 100644 index 000000000..59330ecff --- /dev/null +++ b/db/perf_context_test.cc @@ -0,0 +1,81 @@ +#include + +#include "leveldb/db.h" +#include "leveldb/perf_context.h" +#include "util/testharness.h" + + +namespace leveldb { + +// Path to the database on file system +const std::string kDbName = test::TmpDir() + "/perf_context_test"; + +std::shared_ptr OpenDb() { + DB* db; + Options options; + options.create_if_missing = true; + options.write_buffer_size = 1000000000; // give it a big memtable + Status s = DB::Open(options, kDbName, &db); + ASSERT_OK(s); + return std::shared_ptr(db); +} + +class PerfContextTest { }; + +int kTotalKeys = 100; + +TEST(PerfContextTest, KeyComparisonCount) { + + DestroyDB(kDbName, Options()); // Start this test with a fresh DB + + auto db = OpenDb(); + + WriteOptions write_options; + ReadOptions read_options; + + uint64_t total_user_key_comparison_get = 0; + uint64_t total_user_key_comparison_put = 0; + uint64_t max_user_key_comparison_get = 0; + + std::cout << "Inserting " << kTotalKeys << " key/value pairs\n...\n"; + + for (int i = 0; i < kTotalKeys; ++i) { + std::string key = "k" + std::to_string(i); + std::string value = "v" + std::to_string(i); + + perf_context.Reset(); + db->Put(write_options, key, value); + total_user_key_comparison_put += perf_context.user_key_comparison_count; + + perf_context.Reset(); + db->Get(read_options, key, &value); + total_user_key_comparison_get += perf_context.user_key_comparison_count; + max_user_key_comparison_get = + std::max(max_user_key_comparison_get, + perf_context.user_key_comparison_count); + } + + std::cout << "total user key comparison get: " + << total_user_key_comparison_get << "\n" + << "total user key comparison put: " + << total_user_key_comparison_put << "\n" + << "max user key comparison get: " + << max_user_key_comparison_get << "\n" + << "avg user key comparison get:" + << total_user_key_comparison_get/kTotalKeys << "\n"; + +} + + +} + + +int main(int argc, char** argv) { + + if (argc > 1) { + leveldb::kTotalKeys = std::stoi(argv[1]); + } + + leveldb::test::RunAllTests(); + return 0; +} diff --git a/include/leveldb/perf_context.h b/include/leveldb/perf_context.h new file mode 100644 index 000000000..bbac3601f --- /dev/null +++ b/include/leveldb/perf_context.h @@ -0,0 +1,24 @@ +#ifndef STORAGE_LEVELDB_INCLUDE_PERF_CONTEXT_H +#define STORAGE_LEVELDB_INCLUDE_PERF_CONTEXT_H + +#include + +namespace leveldb { + +// A thread local context for gathering performance counter efficiently +// and transparently. + +struct PerfContext { + + void Reset(); // reset all performance counters to zero + + + uint64_t user_key_comparison_count; // total number of user key comparisons +}; + +extern __thread PerfContext perf_context; + +} + + +#endif