Skip to content

Commit

Permalink
Add PerfContextByLevel to provide per level perf context information (f…
Browse files Browse the repository at this point in the history
…acebook#4226)

Summary:
Current implementation of perf context is level agnostic. Making it hard to do performance evaluation for the LSM tree. This PR adds `PerfContextByLevel` to decompose the counters by level.
This will be helpful when analyzing point and range query performance as well as tuning bloom filter
Also replaced __thread with thread_local keyword for perf_context
Pull Request resolved: facebook#4226

Differential Revision: D10369509

Pulled By: miasantreble

fbshipit-source-id: f1ced4e0de5fcebdb7f9cff36164516bc6382d82
  • Loading branch information
miasantreble authored and facebook-github-bot committed Oct 17, 2018
1 parent 1e38458 commit d6ec288
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 6 deletions.
3 changes: 2 additions & 1 deletion HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Rocksdb Change Log
## Unreleased
### New Features
* Introduced CacheAllocator, which lets the user specify custom allocator for memory in block cache.
* Introduced `CacheAllocator`, which lets the user specify custom allocator for memory in block cache.
* Introduced `PerfContextByLevel` as part of `PerfContext` which allows storing perf context at each level. Also replaced `__thread` with `thread_local` keyword for perf_context.

### Bug Fixes
* Fix corner case where a write group leader blocked due to write stall blocks other writers in queue with WriteOptions::no_slowdown set.
Expand Down
23 changes: 23 additions & 0 deletions db/db_bloom_filter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloomCustomPrefixExtractor) {
options.prefix_extractor =
std::make_shared<SliceTransformLimitedDomainGeneric>();
options.statistics = rocksdb::CreateDBStatistics();
get_perf_context()->EnablePerLevelPerfContext();
BlockBasedTableOptions bbto;
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
if (partition_filters) {
Expand All @@ -171,20 +172,29 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloomCustomPrefixExtractor) {

ASSERT_EQ("foo", Get("barbarbar"));
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
// TODO (Zhongyi): uncomment the asserts involving level_to_perf_context when per
// level perf context is enabled in block based table reader
// ASSERT_EQ(0, (*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
ASSERT_EQ("foo2", Get("barbarbar2"));
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
// ASSERT_EQ(0, (*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
ASSERT_EQ("NOT_FOUND", Get("barbarbar3"));
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
// ASSERT_EQ(0, (*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);

ASSERT_EQ("NOT_FOUND", Get("barfoofoo"));
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
// ASSERT_EQ(1, (*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);

ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
// ASSERT_EQ(2, (*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);

ro.total_order_seek = true;
ASSERT_TRUE(db_->Get(ro, "foobarbar", &value).IsNotFound());
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
// ASSERT_EQ(2, (*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
get_perf_context()->Reset();
}
}

Expand All @@ -193,6 +203,7 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloom) {
Options options = last_options_;
options.prefix_extractor.reset(NewFixedPrefixTransform(8));
options.statistics = rocksdb::CreateDBStatistics();
get_perf_context()->EnablePerLevelPerfContext();
BlockBasedTableOptions bbto;
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
if (partition_filters) {
Expand Down Expand Up @@ -231,6 +242,8 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloom) {
ro.total_order_seek = true;
ASSERT_TRUE(db_->Get(ro, "foobarbar", &value).IsNotFound());
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
// ASSERT_EQ(2, (*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
get_perf_context()->Reset();
}
}

Expand All @@ -239,6 +252,7 @@ TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
Options options = last_options_;
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
options.statistics = rocksdb::CreateDBStatistics();
get_perf_context()->EnablePerLevelPerfContext();

BlockBasedTableOptions bbto;
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
Expand Down Expand Up @@ -386,6 +400,8 @@ TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
ASSERT_EQ("bar", Get("barfoo"));
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
// ASSERT_EQ(12, (*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
get_perf_context()->Reset();
}
}

Expand Down Expand Up @@ -490,6 +506,7 @@ TEST_F(DBBloomFilterTest, BloomFilterRate) {
while (ChangeFilterOptions()) {
Options options = CurrentOptions();
options.statistics = rocksdb::CreateDBStatistics();
get_perf_context()->EnablePerLevelPerfContext();
CreateAndReopenWithCF({"pikachu"}, options);

const int maxKey = 10000;
Expand All @@ -511,6 +528,8 @@ TEST_F(DBBloomFilterTest, BloomFilterRate) {
ASSERT_EQ("NOT_FOUND", Get(1, Key(i + 33333)));
}
ASSERT_GE(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), maxKey * 0.98);
// ASSERT_GE((*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful, maxKey*0.98);
get_perf_context()->Reset();
}
}

Expand Down Expand Up @@ -1032,6 +1051,7 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
options.optimize_filters_for_hits = true;
options.statistics = rocksdb::CreateDBStatistics();
get_perf_context()->EnablePerLevelPerfContext();
CreateAndReopenWithCF({"mypikachu"}, options);

int numkeys = 200000;
Expand Down Expand Up @@ -1078,6 +1098,8 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
// no bloom filter. Most keys be checked bloom filters twice.
ASSERT_GT(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 65000 * 2);
ASSERT_LT(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 120000 * 2);
// ASSERT_GT((*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful, 65000*2);
// ASSERT_LT((*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful, 120000*2);

for (int i = 0; i < numkeys; i += 2) {
ASSERT_EQ(Get(1, Key(i)), "val");
Expand Down Expand Up @@ -1187,6 +1209,7 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
ASSERT_EQ(2 /* index and data block */,
TestGetTickerCount(options, BLOCK_CACHE_ADD));
get_perf_context()->Reset();
}

int CountIter(std::unique_ptr<Iterator>& iter, const Slice& key) {
Expand Down
34 changes: 31 additions & 3 deletions db/perf_context_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#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"
Expand Down Expand Up @@ -579,18 +580,18 @@ TEST_F(PerfContextTest, SeekKeyComparison) {

TEST_F(PerfContextTest, DBMutexLockCounter) {
int stats_code[] = {0, static_cast<int>(DB_MUTEX_WAIT_MICROS)};
for (PerfLevel perf_level :
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::port::Thread child_thread([&] {
SetPerfLevel(perf_level);
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 == PerfLevel::kEnableTimeExceptForMutex ||
if (perf_level_test == PerfLevel::kEnableTimeExceptForMutex ||
stats_code[c] != DB_MUTEX_WAIT_MICROS) {
ASSERT_EQ(get_perf_context()->db_mutex_lock_nanos, 0);
} else {
Expand Down Expand Up @@ -686,7 +687,34 @@ TEST_F(PerfContextTest, MergeOperatorTime) {

delete db;
}

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);
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);
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"));
}
} // namespace rocksdb

int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
Expand Down
26 changes: 26 additions & 0 deletions include/rocksdb/perf_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#pragma once

#include <map>
#include <stdint.h>
#include <string>

Expand All @@ -16,12 +17,35 @@ namespace rocksdb {
// and transparently.
// Use SetPerfLevel(PerfLevel::kEnableTime) to enable time stats.

struct PerfContextByLevel {
// # of times bloom filter has avoided file reads, i.e., negatives.
uint64_t bloom_filter_useful = 0;
// # of times bloom FullFilter has not avoided the reads.
uint64_t bloom_filter_full_positive = 0;
// # of times bloom FullFilter has not avoided the reads and data actually
// exist.
uint64_t bloom_filter_full_true_positive = 0;

void Reset(); // reset all performance counters to zero
};

struct PerfContext {

~PerfContext();

void Reset(); // reset all performance counters to zero

std::string ToString(bool exclude_zero_counters = false) const;

// enable per level perf context and allocate storage for PerfContextByLevel
void EnablePerLevelPerfContext();

// temporarily disable per level perf contxt by setting the flag to false
void DisablePerLevelPerfContext();

// free the space for PerfContextByLevel, also disable per level perf context
void ClearPerLevelPerfContext();

uint64_t user_key_comparison_count; // total number of user key comparisons
uint64_t block_cache_hit_count; // total number of block cache hits
uint64_t block_read_count; // total number of block reads (with IO)
Expand Down Expand Up @@ -168,6 +192,8 @@ struct PerfContext {
uint64_t env_lock_file_nanos;
uint64_t env_unlock_file_nanos;
uint64_t env_new_logger_nanos;
std::map<uint32_t, PerfContextByLevel>* level_to_perf_context;
bool per_level_perf_context_enabled;
};

// Get Thread-local PerfContext object pointer
Expand Down
54 changes: 53 additions & 1 deletion monitoring/perf_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ PerfContext perf_context;
#if defined(OS_SOLARIS)
__thread PerfContext perf_context_;
#else
__thread PerfContext perf_context;
thread_local PerfContext perf_context;
#endif
#endif

Expand All @@ -31,6 +31,12 @@ PerfContext* get_perf_context() {
#endif
}

PerfContext::~PerfContext() {
#if !defined(NPERF_CONTEXT) && defined(ROCKSDB_SUPPORT_THREAD_LOCAL) && !defined(OS_SOLARIS)
ClearPerLevelPerfContext();
#endif
}

void PerfContext::Reset() {
#ifndef NPERF_CONTEXT
user_key_comparison_count = 0;
Expand Down Expand Up @@ -104,6 +110,11 @@ void PerfContext::Reset() {
env_lock_file_nanos = 0;
env_unlock_file_nanos = 0;
env_new_logger_nanos = 0;
if (per_level_perf_context_enabled && level_to_perf_context) {
for (auto& kv : *level_to_perf_context) {
kv.second.Reset();
}
}
#endif
}

Expand All @@ -112,6 +123,25 @@ void PerfContext::Reset() {
ss << #counter << " = " << counter << ", "; \
}

#define PERF_CONTEXT_BY_LEVEL_OUTPUT_ONE_COUNTER(counter) \
if (per_level_perf_context_enabled && \
level_to_perf_context) { \
ss << #counter << " = "; \
for (auto& kv : *level_to_perf_context) { \
if (!exclude_zero_counters || (kv.second.counter > 0)) { \
ss << kv.second.counter << "@level" << kv.first << ", "; \
} \
} \
}

void PerfContextByLevel::Reset() {
#ifndef NPERF_CONTEXT
bloom_filter_useful = 0;
bloom_filter_full_positive = 0;
bloom_filter_full_true_positive = 0;
#endif
}

std::string PerfContext::ToString(bool exclude_zero_counters) const {
#ifdef NPERF_CONTEXT
return "";
Expand Down Expand Up @@ -186,8 +216,30 @@ std::string PerfContext::ToString(bool exclude_zero_counters) const {
PERF_CONTEXT_OUTPUT(env_lock_file_nanos);
PERF_CONTEXT_OUTPUT(env_unlock_file_nanos);
PERF_CONTEXT_OUTPUT(env_new_logger_nanos);
PERF_CONTEXT_BY_LEVEL_OUTPUT_ONE_COUNTER(bloom_filter_useful);
PERF_CONTEXT_BY_LEVEL_OUTPUT_ONE_COUNTER(bloom_filter_full_positive);
PERF_CONTEXT_BY_LEVEL_OUTPUT_ONE_COUNTER(bloom_filter_full_true_positive);
return ss.str();
#endif
}

void PerfContext::EnablePerLevelPerfContext() {
if (!level_to_perf_context) {
level_to_perf_context = new std::map<uint32_t, PerfContextByLevel>();
}
per_level_perf_context_enabled = true;
}

void PerfContext::DisablePerLevelPerfContext(){
per_level_perf_context_enabled = false;
}

void PerfContext::ClearPerLevelPerfContext(){
if (level_to_perf_context) {
delete level_to_perf_context;
level_to_perf_context = nullptr;
}
per_level_perf_context_enabled = false;
}

}
18 changes: 17 additions & 1 deletion monitoring/perf_context_imp.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ extern PerfContext perf_context;
extern __thread PerfContext perf_context_;
#define perf_context (*get_perf_context())
#else
extern __thread PerfContext perf_context;
extern thread_local PerfContext perf_context;
#endif
#endif

Expand Down Expand Up @@ -59,6 +59,22 @@ extern __thread PerfContext perf_context;
perf_context.metric += value; \
}

// Increase metric value
#define PERF_COUNTER_BY_LEVEL_ADD(metric, value, level) \
if (perf_level >= PerfLevel::kEnableCount && \
perf_context.per_level_perf_context_enabled && \
perf_context.level_to_perf_context) { \
if ((*(perf_context.level_to_perf_context)).find(level) != \
(*(perf_context.level_to_perf_context)).end()) { \
(*(perf_context.level_to_perf_context))[level].metric += value; \
} \
else { \
PerfContextByLevel empty_context; \
(*(perf_context.level_to_perf_context))[level] = empty_context; \
(*(perf_context.level_to_perf_context))[level].metric += value; \
} \
} \

#endif

}
Loading

0 comments on commit d6ec288

Please sign in to comment.