cache_bench.cc 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #ifndef GFLAGS
  6. #include <cstdio>
  7. int main() {
  8. fprintf(stderr, "Please install gflags to run rocksdb tools\n");
  9. return 1;
  10. }
  11. #else
  12. #include <stdio.h>
  13. #include <sys/types.h>
  14. #include <cinttypes>
  15. #include "port/port.h"
  16. #include "rocksdb/cache.h"
  17. #include "rocksdb/db.h"
  18. #include "rocksdb/env.h"
  19. #include "util/gflags_compat.h"
  20. #include "util/mutexlock.h"
  21. #include "util/random.h"
  22. using GFLAGS_NAMESPACE::ParseCommandLineFlags;
  23. static const uint32_t KB = 1024;
  24. DEFINE_int32(threads, 16, "Number of concurrent threads to run.");
  25. DEFINE_int64(cache_size, 8 * KB * KB,
  26. "Number of bytes to use as a cache of uncompressed data.");
  27. DEFINE_int32(num_shard_bits, 4, "shard_bits.");
  28. DEFINE_int64(max_key, 1 * KB * KB * KB, "Max number of key to place in cache");
  29. DEFINE_uint64(ops_per_thread, 1200000, "Number of operations per thread.");
  30. DEFINE_bool(populate_cache, false, "Populate cache before operations");
  31. DEFINE_int32(insert_percent, 40,
  32. "Ratio of insert to total workload (expressed as a percentage)");
  33. DEFINE_int32(lookup_percent, 50,
  34. "Ratio of lookup to total workload (expressed as a percentage)");
  35. DEFINE_int32(erase_percent, 10,
  36. "Ratio of erase to total workload (expressed as a percentage)");
  37. DEFINE_bool(use_clock_cache, false, "");
  38. namespace ROCKSDB_NAMESPACE {
  39. class CacheBench;
  40. namespace {
  41. void deleter(const Slice& /*key*/, void* value) {
  42. delete reinterpret_cast<char *>(value);
  43. }
  44. // State shared by all concurrent executions of the same benchmark.
  45. class SharedState {
  46. public:
  47. explicit SharedState(CacheBench* cache_bench)
  48. : cv_(&mu_),
  49. num_threads_(FLAGS_threads),
  50. num_initialized_(0),
  51. start_(false),
  52. num_done_(0),
  53. cache_bench_(cache_bench) {
  54. }
  55. ~SharedState() {}
  56. port::Mutex* GetMutex() {
  57. return &mu_;
  58. }
  59. port::CondVar* GetCondVar() {
  60. return &cv_;
  61. }
  62. CacheBench* GetCacheBench() const {
  63. return cache_bench_;
  64. }
  65. void IncInitialized() {
  66. num_initialized_++;
  67. }
  68. void IncDone() {
  69. num_done_++;
  70. }
  71. bool AllInitialized() const {
  72. return num_initialized_ >= num_threads_;
  73. }
  74. bool AllDone() const {
  75. return num_done_ >= num_threads_;
  76. }
  77. void SetStart() {
  78. start_ = true;
  79. }
  80. bool Started() const {
  81. return start_;
  82. }
  83. private:
  84. port::Mutex mu_;
  85. port::CondVar cv_;
  86. const uint64_t num_threads_;
  87. uint64_t num_initialized_;
  88. bool start_;
  89. uint64_t num_done_;
  90. CacheBench* cache_bench_;
  91. };
  92. // Per-thread state for concurrent executions of the same benchmark.
  93. struct ThreadState {
  94. uint32_t tid;
  95. Random rnd;
  96. SharedState* shared;
  97. ThreadState(uint32_t index, SharedState* _shared)
  98. : tid(index), rnd(1000 + index), shared(_shared) {}
  99. };
  100. } // namespace
  101. class CacheBench {
  102. public:
  103. CacheBench() : num_threads_(FLAGS_threads) {
  104. if (FLAGS_use_clock_cache) {
  105. cache_ = NewClockCache(FLAGS_cache_size, FLAGS_num_shard_bits);
  106. if (!cache_) {
  107. fprintf(stderr, "Clock cache not supported.\n");
  108. exit(1);
  109. }
  110. } else {
  111. cache_ = NewLRUCache(FLAGS_cache_size, FLAGS_num_shard_bits);
  112. }
  113. }
  114. ~CacheBench() {}
  115. void PopulateCache() {
  116. Random rnd(1);
  117. for (int64_t i = 0; i < FLAGS_cache_size; i++) {
  118. uint64_t rand_key = rnd.Next() % FLAGS_max_key;
  119. // Cast uint64* to be char*, data would be copied to cache
  120. Slice key(reinterpret_cast<char*>(&rand_key), 8);
  121. // do insert
  122. cache_->Insert(key, new char[10], 1, &deleter);
  123. }
  124. }
  125. bool Run() {
  126. ROCKSDB_NAMESPACE::Env* env = ROCKSDB_NAMESPACE::Env::Default();
  127. PrintEnv();
  128. SharedState shared(this);
  129. std::vector<ThreadState*> threads(num_threads_);
  130. for (uint32_t i = 0; i < num_threads_; i++) {
  131. threads[i] = new ThreadState(i, &shared);
  132. env->StartThread(ThreadBody, threads[i]);
  133. }
  134. {
  135. MutexLock l(shared.GetMutex());
  136. while (!shared.AllInitialized()) {
  137. shared.GetCondVar()->Wait();
  138. }
  139. // Record start time
  140. uint64_t start_time = env->NowMicros();
  141. // Start all threads
  142. shared.SetStart();
  143. shared.GetCondVar()->SignalAll();
  144. // Wait threads to complete
  145. while (!shared.AllDone()) {
  146. shared.GetCondVar()->Wait();
  147. }
  148. // Record end time
  149. uint64_t end_time = env->NowMicros();
  150. double elapsed = static_cast<double>(end_time - start_time) * 1e-6;
  151. uint32_t qps = static_cast<uint32_t>(
  152. static_cast<double>(FLAGS_threads * FLAGS_ops_per_thread) / elapsed);
  153. fprintf(stdout, "Complete in %.3f s; QPS = %u\n", elapsed, qps);
  154. }
  155. return true;
  156. }
  157. private:
  158. std::shared_ptr<Cache> cache_;
  159. uint32_t num_threads_;
  160. static void ThreadBody(void* v) {
  161. ThreadState* thread = reinterpret_cast<ThreadState*>(v);
  162. SharedState* shared = thread->shared;
  163. {
  164. MutexLock l(shared->GetMutex());
  165. shared->IncInitialized();
  166. if (shared->AllInitialized()) {
  167. shared->GetCondVar()->SignalAll();
  168. }
  169. while (!shared->Started()) {
  170. shared->GetCondVar()->Wait();
  171. }
  172. }
  173. thread->shared->GetCacheBench()->OperateCache(thread);
  174. {
  175. MutexLock l(shared->GetMutex());
  176. shared->IncDone();
  177. if (shared->AllDone()) {
  178. shared->GetCondVar()->SignalAll();
  179. }
  180. }
  181. }
  182. void OperateCache(ThreadState* thread) {
  183. for (uint64_t i = 0; i < FLAGS_ops_per_thread; i++) {
  184. uint64_t rand_key = thread->rnd.Next() % FLAGS_max_key;
  185. // Cast uint64* to be char*, data would be copied to cache
  186. Slice key(reinterpret_cast<char*>(&rand_key), 8);
  187. int32_t prob_op = thread->rnd.Uniform(100);
  188. if (prob_op >= 0 && prob_op < FLAGS_insert_percent) {
  189. // do insert
  190. cache_->Insert(key, new char[10], 1, &deleter);
  191. } else if (prob_op -= FLAGS_insert_percent &&
  192. prob_op < FLAGS_lookup_percent) {
  193. // do lookup
  194. auto handle = cache_->Lookup(key);
  195. if (handle) {
  196. cache_->Release(handle);
  197. }
  198. } else if (prob_op -= FLAGS_lookup_percent &&
  199. prob_op < FLAGS_erase_percent) {
  200. // do erase
  201. cache_->Erase(key);
  202. }
  203. }
  204. }
  205. void PrintEnv() const {
  206. printf("RocksDB version : %d.%d\n", kMajorVersion, kMinorVersion);
  207. printf("Number of threads : %d\n", FLAGS_threads);
  208. printf("Ops per thread : %" PRIu64 "\n", FLAGS_ops_per_thread);
  209. printf("Cache size : %" PRIu64 "\n", FLAGS_cache_size);
  210. printf("Num shard bits : %d\n", FLAGS_num_shard_bits);
  211. printf("Max key : %" PRIu64 "\n", FLAGS_max_key);
  212. printf("Populate cache : %d\n", FLAGS_populate_cache);
  213. printf("Insert percentage : %d%%\n", FLAGS_insert_percent);
  214. printf("Lookup percentage : %d%%\n", FLAGS_lookup_percent);
  215. printf("Erase percentage : %d%%\n", FLAGS_erase_percent);
  216. printf("----------------------------\n");
  217. }
  218. };
  219. } // namespace ROCKSDB_NAMESPACE
  220. int main(int argc, char** argv) {
  221. ParseCommandLineFlags(&argc, &argv, true);
  222. if (FLAGS_threads <= 0) {
  223. fprintf(stderr, "threads number <= 0\n");
  224. exit(1);
  225. }
  226. ROCKSDB_NAMESPACE::CacheBench bench;
  227. if (FLAGS_populate_cache) {
  228. bench.PopulateCache();
  229. }
  230. if (bench.Run()) {
  231. return 0;
  232. } else {
  233. return 1;
  234. }
  235. }
  236. #endif // GFLAGS