mirror of https://github.com/facebook/rocksdb.git
cache_bench enhancements for jemalloc etc. (#11758)
Summary: * Add some options to cache_bench to use JemallocNodumpAllocator * Make num_shard_bits option use and report cache-specific defaults * Add a usleep option to sleep between operations, for simulating a workload with more CPU idle/wait time. * Use const& for JemallocAllocatorOptions, to improve API usability (e.g. can bind to temporary `{}`) * InstallStackTraceHandler() Pull Request resolved: https://github.com/facebook/rocksdb/pull/11758 Test Plan: manual Reviewed By: jowlyzhang Differential Revision: D48668479 Pulled By: pdillinger fbshipit-source-id: b6032fbe09444cdb8f1443a5e017d2eea4f6205a
This commit is contained in:
parent
6353c6e2fb
commit
d3420464c3
|
@ -3,7 +3,6 @@
|
||||||
// COPYING file in the root directory) and Apache 2.0 License
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
// (found in the LICENSE.Apache file in the root directory).
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
#include "cache_key.h"
|
|
||||||
#ifdef GFLAGS
|
#ifdef GFLAGS
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
@ -13,9 +12,12 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "cache/cache_key.h"
|
||||||
|
#include "cache/sharded_cache.h"
|
||||||
#include "db/db_impl/db_impl.h"
|
#include "db/db_impl/db_impl.h"
|
||||||
#include "monitoring/histogram.h"
|
#include "monitoring/histogram.h"
|
||||||
#include "port/port.h"
|
#include "port/port.h"
|
||||||
|
#include "port/stack_trace.h"
|
||||||
#include "rocksdb/advanced_cache.h"
|
#include "rocksdb/advanced_cache.h"
|
||||||
#include "rocksdb/convenience.h"
|
#include "rocksdb/convenience.h"
|
||||||
#include "rocksdb/db.h"
|
#include "rocksdb/db.h"
|
||||||
|
@ -44,7 +46,8 @@ static constexpr uint64_t GiB = MiB << 10;
|
||||||
DEFINE_uint32(threads, 16, "Number of concurrent threads to run.");
|
DEFINE_uint32(threads, 16, "Number of concurrent threads to run.");
|
||||||
DEFINE_uint64(cache_size, 1 * GiB,
|
DEFINE_uint64(cache_size, 1 * GiB,
|
||||||
"Number of bytes to use as a cache of uncompressed data.");
|
"Number of bytes to use as a cache of uncompressed data.");
|
||||||
DEFINE_uint32(num_shard_bits, 6, "shard_bits.");
|
DEFINE_int32(num_shard_bits, -1,
|
||||||
|
"ShardedCacheOptions::shard_bits. Default = auto");
|
||||||
|
|
||||||
DEFINE_double(resident_ratio, 0.25,
|
DEFINE_double(resident_ratio, 0.25,
|
||||||
"Ratio of keys fitting in cache to keyspace.");
|
"Ratio of keys fitting in cache to keyspace.");
|
||||||
|
@ -76,6 +79,8 @@ DEFINE_uint32(
|
||||||
DEFINE_uint32(gather_stats_entries_per_lock, 256,
|
DEFINE_uint32(gather_stats_entries_per_lock, 256,
|
||||||
"For Cache::ApplyToAllEntries");
|
"For Cache::ApplyToAllEntries");
|
||||||
|
|
||||||
|
DEFINE_uint32(usleep, 0, "Sleep up to this many microseconds after each op.");
|
||||||
|
|
||||||
DEFINE_bool(lean, false,
|
DEFINE_bool(lean, false,
|
||||||
"If true, no additional computation is performed besides cache "
|
"If true, no additional computation is performed besides cache "
|
||||||
"operations.");
|
"operations.");
|
||||||
|
@ -97,6 +102,17 @@ static class std::shared_ptr<ROCKSDB_NAMESPACE::SecondaryCache> secondary_cache;
|
||||||
|
|
||||||
DEFINE_string(cache_type, "lru_cache", "Type of block cache.");
|
DEFINE_string(cache_type, "lru_cache", "Type of block cache.");
|
||||||
|
|
||||||
|
DEFINE_bool(use_jemalloc_no_dump_allocator, false,
|
||||||
|
"Whether to use JemallocNoDumpAllocator");
|
||||||
|
|
||||||
|
DEFINE_uint32(jemalloc_no_dump_allocator_num_arenas,
|
||||||
|
ROCKSDB_NAMESPACE::JemallocAllocatorOptions().num_arenas,
|
||||||
|
"JemallocNodumpAllocator::num_arenas");
|
||||||
|
|
||||||
|
DEFINE_bool(jemalloc_no_dump_allocator_limit_tcache_size,
|
||||||
|
ROCKSDB_NAMESPACE::JemallocAllocatorOptions().limit_tcache_size,
|
||||||
|
"JemallocNodumpAllocator::limit_tcache_size");
|
||||||
|
|
||||||
// ## BEGIN stress_cache_key sub-tool options ##
|
// ## BEGIN stress_cache_key sub-tool options ##
|
||||||
// See class StressCacheKey below.
|
// See class StressCacheKey below.
|
||||||
DEFINE_bool(stress_cache_key, false,
|
DEFINE_bool(stress_cache_key, false,
|
||||||
|
@ -239,8 +255,8 @@ struct KeyGen {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Cache::ObjectPtr createValue(Random64& rnd) {
|
Cache::ObjectPtr createValue(Random64& rnd, MemoryAllocator* alloc) {
|
||||||
char* rv = new char[FLAGS_value_bytes];
|
char* rv = AllocateBlock(FLAGS_value_bytes, alloc).release();
|
||||||
// Fill with some filler data, and take some CPU time
|
// Fill with some filler data, and take some CPU time
|
||||||
for (uint32_t i = 0; i < FLAGS_value_bytes; i += 8) {
|
for (uint32_t i = 0; i < FLAGS_value_bytes; i += 8) {
|
||||||
EncodeFixed64(rv + i, rnd.Next());
|
EncodeFixed64(rv + i, rnd.Next());
|
||||||
|
@ -266,8 +282,8 @@ Status CreateFn(const Slice& data, Cache::CreateContext* /*context*/,
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
};
|
};
|
||||||
|
|
||||||
void DeleteFn(Cache::ObjectPtr value, MemoryAllocator* /*alloc*/) {
|
void DeleteFn(Cache::ObjectPtr value, MemoryAllocator* alloc) {
|
||||||
delete[] static_cast<char*>(value);
|
CustomDeleter{alloc}(static_cast<char*>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Cache::CacheItemHelper helper1_wos(CacheEntryRole::kDataBlock, DeleteFn);
|
Cache::CacheItemHelper helper1_wos(CacheEntryRole::kDataBlock, DeleteFn);
|
||||||
|
@ -302,6 +318,15 @@ class CacheBench {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MemoryAllocator> allocator;
|
||||||
|
if (FLAGS_use_jemalloc_no_dump_allocator) {
|
||||||
|
JemallocAllocatorOptions opts;
|
||||||
|
opts.num_arenas = FLAGS_jemalloc_no_dump_allocator_num_arenas;
|
||||||
|
opts.limit_tcache_size =
|
||||||
|
FLAGS_jemalloc_no_dump_allocator_limit_tcache_size;
|
||||||
|
Status s = NewJemallocNodumpAllocator(opts, &allocator);
|
||||||
|
assert(s.ok());
|
||||||
|
}
|
||||||
if (FLAGS_cache_type == "clock_cache") {
|
if (FLAGS_cache_type == "clock_cache") {
|
||||||
fprintf(stderr, "Old clock cache implementation has been removed.\n");
|
fprintf(stderr, "Old clock cache implementation has been removed.\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -309,6 +334,7 @@ class CacheBench {
|
||||||
HyperClockCacheOptions opts(
|
HyperClockCacheOptions opts(
|
||||||
FLAGS_cache_size, /*estimated_entry_charge=*/0, FLAGS_num_shard_bits);
|
FLAGS_cache_size, /*estimated_entry_charge=*/0, FLAGS_num_shard_bits);
|
||||||
opts.hash_seed = BitwiseAnd(FLAGS_seed, INT32_MAX);
|
opts.hash_seed = BitwiseAnd(FLAGS_seed, INT32_MAX);
|
||||||
|
opts.memory_allocator = allocator;
|
||||||
if (FLAGS_cache_type == "fixed_hyper_clock_cache" ||
|
if (FLAGS_cache_type == "fixed_hyper_clock_cache" ||
|
||||||
FLAGS_cache_type == "hyper_clock_cache") {
|
FLAGS_cache_type == "hyper_clock_cache") {
|
||||||
opts.estimated_entry_charge = FLAGS_value_bytes_estimate > 0
|
opts.estimated_entry_charge = FLAGS_value_bytes_estimate > 0
|
||||||
|
@ -319,7 +345,7 @@ class CacheBench {
|
||||||
opts.min_avg_entry_charge = FLAGS_value_bytes_estimate;
|
opts.min_avg_entry_charge = FLAGS_value_bytes_estimate;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Cache type not supported.");
|
fprintf(stderr, "Cache type not supported.\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
cache_ = opts.MakeSharedCache();
|
cache_ = opts.MakeSharedCache();
|
||||||
|
@ -328,6 +354,7 @@ class CacheBench {
|
||||||
false /* strict_capacity_limit */,
|
false /* strict_capacity_limit */,
|
||||||
0.5 /* high_pri_pool_ratio */);
|
0.5 /* high_pri_pool_ratio */);
|
||||||
opts.hash_seed = BitwiseAnd(FLAGS_seed, INT32_MAX);
|
opts.hash_seed = BitwiseAnd(FLAGS_seed, INT32_MAX);
|
||||||
|
opts.memory_allocator = allocator;
|
||||||
if (!FLAGS_secondary_cache_uri.empty()) {
|
if (!FLAGS_secondary_cache_uri.empty()) {
|
||||||
Status s = SecondaryCache::CreateFromString(
|
Status s = SecondaryCache::CreateFromString(
|
||||||
ConfigOptions(), FLAGS_secondary_cache_uri, &secondary_cache);
|
ConfigOptions(), FLAGS_secondary_cache_uri, &secondary_cache);
|
||||||
|
@ -343,7 +370,7 @@ class CacheBench {
|
||||||
|
|
||||||
cache_ = NewLRUCache(opts);
|
cache_ = NewLRUCache(opts);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Cache type not supported.");
|
fprintf(stderr, "Cache type not supported.\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,7 +400,8 @@ class CacheBench {
|
||||||
keys_since_last_not_found = 0;
|
keys_since_last_not_found = 0;
|
||||||
|
|
||||||
Status s =
|
Status s =
|
||||||
cache_->Insert(key, createValue(rnd), &helper1, FLAGS_value_bytes);
|
cache_->Insert(key, createValue(rnd, cache_->memory_allocator()),
|
||||||
|
&helper1, FLAGS_value_bytes);
|
||||||
assert(s.ok());
|
assert(s.ok());
|
||||||
|
|
||||||
handle = cache_->Lookup(key);
|
handle = cache_->Lookup(key);
|
||||||
|
@ -610,6 +638,7 @@ class CacheBench {
|
||||||
const auto clock = SystemClock::Default().get();
|
const auto clock = SystemClock::Default().get();
|
||||||
uint64_t start_time = clock->NowMicros();
|
uint64_t start_time = clock->NowMicros();
|
||||||
StopWatchNano timer(clock);
|
StopWatchNano timer(clock);
|
||||||
|
auto system_clock = SystemClock::Default();
|
||||||
|
|
||||||
for (uint64_t i = 0; i < FLAGS_ops_per_thread; i++) {
|
for (uint64_t i = 0; i < FLAGS_ops_per_thread; i++) {
|
||||||
Slice key = gen.GetRand(thread->rnd, max_key_, FLAGS_skew);
|
Slice key = gen.GetRand(thread->rnd, max_key_, FLAGS_skew);
|
||||||
|
@ -637,8 +666,9 @@ class CacheBench {
|
||||||
} else {
|
} else {
|
||||||
++lookup_misses;
|
++lookup_misses;
|
||||||
// do insert
|
// do insert
|
||||||
Status s = cache_->Insert(key, createValue(thread->rnd), &helper2,
|
Status s = cache_->Insert(
|
||||||
FLAGS_value_bytes, &handle);
|
key, createValue(thread->rnd, cache_->memory_allocator()),
|
||||||
|
&helper2, FLAGS_value_bytes, &handle);
|
||||||
assert(s.ok());
|
assert(s.ok());
|
||||||
}
|
}
|
||||||
} else if (random_op < insert_threshold_) {
|
} else if (random_op < insert_threshold_) {
|
||||||
|
@ -647,8 +677,9 @@ class CacheBench {
|
||||||
handle = nullptr;
|
handle = nullptr;
|
||||||
}
|
}
|
||||||
// do insert
|
// do insert
|
||||||
Status s = cache_->Insert(key, createValue(thread->rnd), &helper3,
|
Status s = cache_->Insert(
|
||||||
FLAGS_value_bytes, &handle);
|
key, createValue(thread->rnd, cache_->memory_allocator()), &helper3,
|
||||||
|
FLAGS_value_bytes, &handle);
|
||||||
assert(s.ok());
|
assert(s.ok());
|
||||||
} else if (random_op < lookup_threshold_) {
|
} else if (random_op < lookup_threshold_) {
|
||||||
if (handle) {
|
if (handle) {
|
||||||
|
@ -679,6 +710,13 @@ class CacheBench {
|
||||||
thread->latency_ns_hist.Add(timer.ElapsedNanos());
|
thread->latency_ns_hist.Add(timer.ElapsedNanos());
|
||||||
}
|
}
|
||||||
thread->shared->AddLookupStats(lookup_hits, lookup_misses);
|
thread->shared->AddLookupStats(lookup_hits, lookup_misses);
|
||||||
|
if (FLAGS_usleep > 0) {
|
||||||
|
unsigned us =
|
||||||
|
static_cast<unsigned>(thread->rnd.Uniform(FLAGS_usleep + 1));
|
||||||
|
if (us > 0) {
|
||||||
|
system_clock->SleepForMicroseconds(us);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (FLAGS_early_exit) {
|
if (FLAGS_early_exit) {
|
||||||
MutexLock l(thread->shared->GetMutex());
|
MutexLock l(thread->shared->GetMutex());
|
||||||
|
@ -712,7 +750,9 @@ class CacheBench {
|
||||||
printf("Ops per thread : %" PRIu64 "\n", FLAGS_ops_per_thread);
|
printf("Ops per thread : %" PRIu64 "\n", FLAGS_ops_per_thread);
|
||||||
printf("Cache size : %s\n",
|
printf("Cache size : %s\n",
|
||||||
BytesToHumanString(FLAGS_cache_size).c_str());
|
BytesToHumanString(FLAGS_cache_size).c_str());
|
||||||
printf("Num shard bits : %u\n", FLAGS_num_shard_bits);
|
printf("Num shard bits : %d\n",
|
||||||
|
static_cast_with_check<ShardedCacheBase>(cache_.get())
|
||||||
|
->GetNumShardBits());
|
||||||
printf("Max key : %" PRIu64 "\n", max_key_);
|
printf("Max key : %" PRIu64 "\n", max_key_);
|
||||||
printf("Resident ratio : %g\n", FLAGS_resident_ratio);
|
printf("Resident ratio : %g\n", FLAGS_resident_ratio);
|
||||||
printf("Skew degree : %u\n", FLAGS_skew);
|
printf("Skew degree : %u\n", FLAGS_skew);
|
||||||
|
@ -1032,6 +1072,7 @@ class StressCacheKey {
|
||||||
};
|
};
|
||||||
|
|
||||||
int cache_bench_tool(int argc, char** argv) {
|
int cache_bench_tool(int argc, char** argv) {
|
||||||
|
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||||
ParseCommandLineFlags(&argc, &argv, true);
|
ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
|
||||||
if (FLAGS_stress_cache_key) {
|
if (FLAGS_stress_cache_key) {
|
||||||
|
|
|
@ -81,7 +81,7 @@ struct JemallocAllocatorOptions {
|
||||||
// The tcache normally incurs 0.5M extra memory usage per-thread. The usage
|
// The tcache normally incurs 0.5M extra memory usage per-thread. The usage
|
||||||
// can be reduced by limiting allocation sizes to cache.
|
// can be reduced by limiting allocation sizes to cache.
|
||||||
extern Status NewJemallocNodumpAllocator(
|
extern Status NewJemallocNodumpAllocator(
|
||||||
JemallocAllocatorOptions& options,
|
const JemallocAllocatorOptions& options,
|
||||||
std::shared_ptr<MemoryAllocator>* memory_allocator);
|
std::shared_ptr<MemoryAllocator>* memory_allocator);
|
||||||
|
|
||||||
} // namespace ROCKSDB_NAMESPACE
|
} // namespace ROCKSDB_NAMESPACE
|
||||||
|
|
|
@ -63,7 +63,7 @@ bool JemallocNodumpAllocator::IsSupported(std::string* why) {
|
||||||
}
|
}
|
||||||
|
|
||||||
JemallocNodumpAllocator::JemallocNodumpAllocator(
|
JemallocNodumpAllocator::JemallocNodumpAllocator(
|
||||||
JemallocAllocatorOptions& options)
|
const JemallocAllocatorOptions& options)
|
||||||
: options_(options)
|
: options_(options)
|
||||||
#ifdef ROCKSDB_JEMALLOC_NODUMP_ALLOCATOR
|
#ifdef ROCKSDB_JEMALLOC_NODUMP_ALLOCATOR
|
||||||
,
|
,
|
||||||
|
@ -283,7 +283,7 @@ void JemallocNodumpAllocator::DestroyThreadSpecificCache(void* ptr) {
|
||||||
#endif // ROCKSDB_JEMALLOC_NODUMP_ALLOCATOR
|
#endif // ROCKSDB_JEMALLOC_NODUMP_ALLOCATOR
|
||||||
|
|
||||||
Status NewJemallocNodumpAllocator(
|
Status NewJemallocNodumpAllocator(
|
||||||
JemallocAllocatorOptions& options,
|
const JemallocAllocatorOptions& options,
|
||||||
std::shared_ptr<MemoryAllocator>* memory_allocator) {
|
std::shared_ptr<MemoryAllocator>* memory_allocator) {
|
||||||
if (memory_allocator == nullptr) {
|
if (memory_allocator == nullptr) {
|
||||||
return Status::InvalidArgument("memory_allocator must be non-null.");
|
return Status::InvalidArgument("memory_allocator must be non-null.");
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||||
// arena mutexes.
|
// arena mutexes.
|
||||||
class JemallocNodumpAllocator : public BaseMemoryAllocator {
|
class JemallocNodumpAllocator : public BaseMemoryAllocator {
|
||||||
public:
|
public:
|
||||||
explicit JemallocNodumpAllocator(JemallocAllocatorOptions& options);
|
explicit JemallocNodumpAllocator(const JemallocAllocatorOptions& options);
|
||||||
#ifdef ROCKSDB_JEMALLOC_NODUMP_ALLOCATOR
|
#ifdef ROCKSDB_JEMALLOC_NODUMP_ALLOCATOR
|
||||||
~JemallocNodumpAllocator();
|
~JemallocNodumpAllocator();
|
||||||
#endif // ROCKSDB_JEMALLOC_NODUMP_ALLOCATOR
|
#endif // ROCKSDB_JEMALLOC_NODUMP_ALLOCATOR
|
||||||
|
|
Loading…
Reference in New Issue