Summary: 1. A benchmark for cache Test Plan: ./cache_bench Reviewers: yhchiang, dhruba, sdong, igor, ljin Reviewed By: ljin Subscribers: leveldb Differential Revision: https://reviews.facebook.net/D22809main
parent
9f1c80b556
commit
40ddc3d6c4
@ -0,0 +1,257 @@ |
||||
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS |
||||
#define __STDC_FORMAT_MACROS |
||||
#endif |
||||
#ifndef GFLAGS |
||||
#include <cstdio> |
||||
int main() { |
||||
fprintf(stderr, "Please install gflags to run rocksdb tools\n"); |
||||
return 1; |
||||
} |
||||
#else |
||||
|
||||
#include <sys/types.h> |
||||
#include <stdio.h> |
||||
#include <gflags/gflags.h> |
||||
|
||||
#include "rocksdb/db.h" |
||||
#include "rocksdb/cache.h" |
||||
#include "rocksdb/env.h" |
||||
#include "port/port.h" |
||||
#include "util/mutexlock.h" |
||||
#include "util/random.h" |
||||
|
||||
using GFLAGS::ParseCommandLineFlags; |
||||
|
||||
static const uint32_t KB = 1024; |
||||
|
||||
DEFINE_int32(threads, 10, "Number of concurrent threads to run."); |
||||
DEFINE_int64(cache_size, 2 * KB * KB * KB, |
||||
"Number of bytes to use as a cache of uncompressed data."); |
||||
DEFINE_int32(num_shard_bits, 4, "shard_bits."); |
||||
|
||||
DEFINE_int64(max_key, 1 * KB* KB, "Max number of key to place in cache"); |
||||
DEFINE_uint64(ops_per_thread, 1200000, "Number of operations per thread."); |
||||
|
||||
DEFINE_int32(insert_percent, 40, |
||||
"Ratio of insert to total workload (expressed as a percentage)"); |
||||
DEFINE_int32(lookup_percent, 50, |
||||
"Ratio of lookup to total workload (expressed as a percentage)"); |
||||
DEFINE_int32(erase_percent, 10, |
||||
"Ratio of erase to total workload (expressed as a percentage)"); |
||||
|
||||
namespace rocksdb { |
||||
|
||||
class CacheBench; |
||||
namespace { |
||||
void deleter(const Slice& key, void* value) { |
||||
delete reinterpret_cast<char *>(value); |
||||
} |
||||
|
||||
// State shared by all concurrent executions of the same benchmark.
|
||||
class SharedState { |
||||
public: |
||||
explicit SharedState(CacheBench* cache_bench) |
||||
: cv_(&mu_), |
||||
num_threads_(FLAGS_threads), |
||||
num_initialized_(0), |
||||
start_(false), |
||||
num_done_(0), |
||||
cache_bench_(cache_bench) { |
||||
} |
||||
|
||||
~SharedState() {} |
||||
|
||||
port::Mutex* GetMutex() { |
||||
return &mu_; |
||||
} |
||||
|
||||
port::CondVar* GetCondVar() { |
||||
return &cv_; |
||||
} |
||||
|
||||
CacheBench* GetCacheBench() const { |
||||
return cache_bench_; |
||||
} |
||||
|
||||
void IncInitialized() { |
||||
num_initialized_++; |
||||
} |
||||
|
||||
void IncDone() { |
||||
num_done_++; |
||||
} |
||||
|
||||
bool AllInitialized() const { |
||||
return num_initialized_ >= num_threads_; |
||||
} |
||||
|
||||
bool AllDone() const { |
||||
return num_done_ >= num_threads_; |
||||
} |
||||
|
||||
void SetStart() { |
||||
start_ = true; |
||||
} |
||||
|
||||
bool Started() const { |
||||
return start_; |
||||
} |
||||
|
||||
private: |
||||
port::Mutex mu_; |
||||
port::CondVar cv_; |
||||
|
||||
const uint64_t num_threads_; |
||||
uint64_t num_initialized_; |
||||
bool start_; |
||||
uint64_t num_done_; |
||||
|
||||
CacheBench* cache_bench_; |
||||
}; |
||||
|
||||
// Per-thread state for concurrent executions of the same benchmark.
|
||||
struct ThreadState { |
||||
uint32_t tid; |
||||
Random rnd; |
||||
SharedState* shared; |
||||
|
||||
ThreadState(uint32_t index, SharedState *shared) |
||||
: tid(index), |
||||
rnd(1000 + index), |
||||
shared(shared) {} |
||||
}; |
||||
} // namespace
|
||||
|
||||
class CacheBench { |
||||
public: |
||||
CacheBench() : |
||||
cache_(NewLRUCache(FLAGS_cache_size, FLAGS_num_shard_bits)), |
||||
num_threads_(FLAGS_threads) {} |
||||
|
||||
~CacheBench() {} |
||||
|
||||
bool Run() { |
||||
rocksdb::Env* env = rocksdb::Env::Default(); |
||||
|
||||
PrintEnv(); |
||||
SharedState shared(this); |
||||
std::vector<ThreadState*> threads(num_threads_); |
||||
for (uint32_t i = 0; i < num_threads_; i++) { |
||||
threads[i] = new ThreadState(i, &shared); |
||||
env->StartThread(ThreadBody, threads[i]); |
||||
} |
||||
{ |
||||
MutexLock l(shared.GetMutex()); |
||||
while (!shared.AllInitialized()) { |
||||
shared.GetCondVar()->Wait(); |
||||
} |
||||
// Record start time
|
||||
uint64_t start_time = env->NowMicros(); |
||||
|
||||
// Start all threads
|
||||
shared.SetStart(); |
||||
shared.GetCondVar()->SignalAll(); |
||||
|
||||
// Wait threads to complete
|
||||
while (!shared.AllDone()) { |
||||
shared.GetCondVar()->Wait(); |
||||
} |
||||
|
||||
// Record end time
|
||||
uint64_t end_time = env->NowMicros(); |
||||
fprintf(stdout, "Complete in %" PRIu64 "ms\n", end_time - start_time); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
private: |
||||
std::shared_ptr<Cache> cache_; |
||||
uint32_t num_threads_; |
||||
|
||||
static void ThreadBody(void* v) { |
||||
ThreadState* thread = reinterpret_cast<ThreadState*>(v); |
||||
SharedState* shared = thread->shared; |
||||
|
||||
{ |
||||
MutexLock l(shared->GetMutex()); |
||||
shared->IncInitialized(); |
||||
if (shared->AllInitialized()) { |
||||
shared->GetCondVar()->SignalAll(); |
||||
} |
||||
while (!shared->Started()) { |
||||
shared->GetCondVar()->Wait(); |
||||
} |
||||
} |
||||
thread->shared->GetCacheBench()->OperateCache(thread); |
||||
|
||||
{ |
||||
MutexLock l(shared->GetMutex()); |
||||
shared->IncDone(); |
||||
if (shared->AllDone()) { |
||||
shared->GetCondVar()->SignalAll(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void OperateCache(ThreadState* thread) { |
||||
for (uint64_t i = 0; i < FLAGS_ops_per_thread; i++) { |
||||
uint64_t rand_key = thread->rnd.Next() % FLAGS_max_key; |
||||
// Cast uint64* to be char*, data would be copied to cache
|
||||
Slice key(reinterpret_cast<char*>(&rand_key), 8); |
||||
int32_t prob_op = thread->rnd.Uniform(100); |
||||
if (prob_op >= 0 && prob_op < FLAGS_insert_percent) { |
||||
// do insert
|
||||
auto handle = cache_->Insert(key, new char[10], 1, &deleter); |
||||
cache_->Release(handle); |
||||
} else if (prob_op -= FLAGS_insert_percent && |
||||
prob_op < FLAGS_lookup_percent) { |
||||
// do lookup
|
||||
auto handle = cache_->Lookup(key); |
||||
if (handle) { |
||||
cache_->Release(handle); |
||||
} |
||||
} else if (prob_op -= FLAGS_lookup_percent && |
||||
prob_op < FLAGS_erase_percent) { |
||||
// do erase
|
||||
cache_->Erase(key); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void PrintEnv() const { |
||||
printf("RocksDB version : %d.%d\n", kMajorVersion, kMinorVersion); |
||||
printf("Number of threads : %d\n", FLAGS_threads); |
||||
printf("Ops per thread : %" PRIu64 "\n", FLAGS_ops_per_thread); |
||||
printf("Cache size : %" PRIu64 "\n", FLAGS_cache_size); |
||||
printf("Num shard bits : %d\n", FLAGS_num_shard_bits); |
||||
printf("Max key : %" PRIu64 "\n", FLAGS_max_key); |
||||
printf("Insert percentage : %d%%\n", FLAGS_insert_percent); |
||||
printf("Lookup percentage : %d%%\n", FLAGS_lookup_percent); |
||||
printf("Erase percentage : %d%%\n", FLAGS_erase_percent); |
||||
printf("----------------------------\n"); |
||||
} |
||||
}; |
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { |
||||
ParseCommandLineFlags(&argc, &argv, true); |
||||
|
||||
if (FLAGS_threads <= 0) { |
||||
fprintf(stderr, "threads number <= 0\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
rocksdb::CacheBench bench; |
||||
if (bench.Run()) { |
||||
return 0; |
||||
} else { |
||||
return 1; |
||||
} |
||||
} |
||||
|
||||
#endif // GFLAGS
|
Loading…
Reference in new issue