db_bench.cc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996
  1. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  4. #include <sys/types.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include "leveldb/cache.h"
  8. #include "leveldb/db.h"
  9. #include "leveldb/env.h"
  10. #include "leveldb/filter_policy.h"
  11. #include "leveldb/write_batch.h"
  12. #include "port/port.h"
  13. #include "util/crc32c.h"
  14. #include "util/histogram.h"
  15. #include "util/mutexlock.h"
  16. #include "util/random.h"
  17. #include "util/testutil.h"
  18. // Comma-separated list of operations to run in the specified order
  19. // Actual benchmarks:
  20. // fillseq -- write N values in sequential key order in async mode
  21. // fillrandom -- write N values in random key order in async mode
  22. // overwrite -- overwrite N values in random key order in async mode
  23. // fillsync -- write N/100 values in random key order in sync mode
  24. // fill100K -- write N/1000 100K values in random order in async mode
  25. // deleteseq -- delete N keys in sequential order
  26. // deleterandom -- delete N keys in random order
  27. // readseq -- read N times sequentially
  28. // readreverse -- read N times in reverse order
  29. // readrandom -- read N times in random order
  30. // readmissing -- read N missing keys in random order
  31. // readhot -- read N times in random order from 1% section of DB
  32. // seekrandom -- N random seeks
  33. // open -- cost of opening a DB
  34. // crc32c -- repeated crc32c of 4K of data
  35. // Meta operations:
  36. // compact -- Compact the entire DB
  37. // stats -- Print DB stats
  38. // sstables -- Print sstable info
  39. // heapprofile -- Dump a heap profile (if supported by this port)
  40. static const char* FLAGS_benchmarks =
  41. "fillseq,"
  42. "fillsync,"
  43. "fillrandom,"
  44. "overwrite,"
  45. "readrandom,"
  46. "readrandom," // Extra run to allow previous compactions to quiesce
  47. "readseq,"
  48. "readreverse,"
  49. "compact,"
  50. "readrandom,"
  51. "readseq,"
  52. "readreverse,"
  53. "fill100K,"
  54. "crc32c,"
  55. "snappycomp,"
  56. "snappyuncomp,"
  57. ;
  58. // Number of key/values to place in database
  59. static int FLAGS_num = 1000000;
  60. // Number of read operations to do. If negative, do FLAGS_num reads.
  61. static int FLAGS_reads = -1;
  62. // Number of concurrent threads to run.
  63. static int FLAGS_threads = 1;
  64. // Size of each value
  65. static int FLAGS_value_size = 100;
  66. // Arrange to generate values that shrink to this fraction of
  67. // their original size after compression
  68. static double FLAGS_compression_ratio = 0.5;
  69. // Print histogram of operation timings
  70. static bool FLAGS_histogram = false;
  71. // Number of bytes to buffer in memtable before compacting
  72. // (initialized to default value by "main")
  73. static int FLAGS_write_buffer_size = 0;
  74. // Number of bytes written to each file.
  75. // (initialized to default value by "main")
  76. static int FLAGS_max_file_size = 0;
  77. // Approximate size of user data packed per block (before compression.
  78. // (initialized to default value by "main")
  79. static int FLAGS_block_size = 0;
  80. // Number of bytes to use as a cache of uncompressed data.
  81. // Negative means use default settings.
  82. static int FLAGS_cache_size = -1;
  83. // Maximum number of files to keep open at the same time (use default if == 0)
  84. static int FLAGS_open_files = 0;
  85. // Bloom filter bits per key.
  86. // Negative means use default settings.
  87. static int FLAGS_bloom_bits = -1;
  88. // If true, do not destroy the existing database. If you set this
  89. // flag and also specify a benchmark that wants a fresh database, that
  90. // benchmark will fail.
  91. static bool FLAGS_use_existing_db = false;
  92. // If true, reuse existing log/MANIFEST files when re-opening a database.
  93. static bool FLAGS_reuse_logs = false;
  94. // Use the db with the following name.
  95. static const char* FLAGS_db = nullptr;
  96. namespace leveldb {
  97. namespace {
  98. leveldb::Env* g_env = nullptr;
  99. // Helper for quickly generating random data.
  100. class RandomGenerator {
  101. private:
  102. std::string data_;
  103. int pos_;
  104. public:
  105. RandomGenerator() {
  106. // We use a limited amount of data over and over again and ensure
  107. // that it is larger than the compression window (32KB), and also
  108. // large enough to serve all typical value sizes we want to write.
  109. Random rnd(301);
  110. std::string piece;
  111. while (data_.size() < 1048576) {
  112. // Add a short fragment that is as compressible as specified
  113. // by FLAGS_compression_ratio.
  114. test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
  115. data_.append(piece);
  116. }
  117. pos_ = 0;
  118. }
  119. Slice Generate(size_t len) {
  120. if (pos_ + len > data_.size()) {
  121. pos_ = 0;
  122. assert(len < data_.size());
  123. }
  124. pos_ += len;
  125. return Slice(data_.data() + pos_ - len, len);
  126. }
  127. };
  128. #if defined(__linux)
  129. static Slice TrimSpace(Slice s) {
  130. size_t start = 0;
  131. while (start < s.size() && isspace(s[start])) {
  132. start++;
  133. }
  134. size_t limit = s.size();
  135. while (limit > start && isspace(s[limit-1])) {
  136. limit--;
  137. }
  138. return Slice(s.data() + start, limit - start);
  139. }
  140. #endif
  141. static void AppendWithSpace(std::string* str, Slice msg) {
  142. if (msg.empty()) return;
  143. if (!str->empty()) {
  144. str->push_back(' ');
  145. }
  146. str->append(msg.data(), msg.size());
  147. }
  148. class Stats {
  149. private:
  150. double start_;
  151. double finish_;
  152. double seconds_;
  153. int done_;
  154. int next_report_;
  155. int64_t bytes_;
  156. double last_op_finish_;
  157. Histogram hist_;
  158. std::string message_;
  159. public:
  160. Stats() { Start(); }
  161. void Start() {
  162. next_report_ = 100;
  163. last_op_finish_ = start_;
  164. hist_.Clear();
  165. done_ = 0;
  166. bytes_ = 0;
  167. seconds_ = 0;
  168. start_ = g_env->NowMicros();
  169. finish_ = start_;
  170. message_.clear();
  171. }
  172. void Merge(const Stats& other) {
  173. hist_.Merge(other.hist_);
  174. done_ += other.done_;
  175. bytes_ += other.bytes_;
  176. seconds_ += other.seconds_;
  177. if (other.start_ < start_) start_ = other.start_;
  178. if (other.finish_ > finish_) finish_ = other.finish_;
  179. // Just keep the messages from one thread
  180. if (message_.empty()) message_ = other.message_;
  181. }
  182. void Stop() {
  183. finish_ = g_env->NowMicros();
  184. seconds_ = (finish_ - start_) * 1e-6;
  185. }
  186. void AddMessage(Slice msg) {
  187. AppendWithSpace(&message_, msg);
  188. }
  189. void FinishedSingleOp() {
  190. if (FLAGS_histogram) {
  191. double now = g_env->NowMicros();
  192. double micros = now - last_op_finish_;
  193. hist_.Add(micros);
  194. if (micros > 20000) {
  195. fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
  196. fflush(stderr);
  197. }
  198. last_op_finish_ = now;
  199. }
  200. done_++;
  201. if (done_ >= next_report_) {
  202. if (next_report_ < 1000) next_report_ += 100;
  203. else if (next_report_ < 5000) next_report_ += 500;
  204. else if (next_report_ < 10000) next_report_ += 1000;
  205. else if (next_report_ < 50000) next_report_ += 5000;
  206. else if (next_report_ < 100000) next_report_ += 10000;
  207. else if (next_report_ < 500000) next_report_ += 50000;
  208. else next_report_ += 100000;
  209. fprintf(stderr, "... finished %d ops%30s\r", done_, "");
  210. fflush(stderr);
  211. }
  212. }
  213. void AddBytes(int64_t n) {
  214. bytes_ += n;
  215. }
  216. void Report(const Slice& name) {
  217. // Pretend at least one op was done in case we are running a benchmark
  218. // that does not call FinishedSingleOp().
  219. if (done_ < 1) done_ = 1;
  220. std::string extra;
  221. if (bytes_ > 0) {
  222. // Rate is computed on actual elapsed time, not the sum of per-thread
  223. // elapsed times.
  224. double elapsed = (finish_ - start_) * 1e-6;
  225. char rate[100];
  226. snprintf(rate, sizeof(rate), "%6.1f MB/s",
  227. (bytes_ / 1048576.0) / elapsed);
  228. extra = rate;
  229. }
  230. AppendWithSpace(&extra, message_);
  231. fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
  232. name.ToString().c_str(),
  233. seconds_ * 1e6 / done_,
  234. (extra.empty() ? "" : " "),
  235. extra.c_str());
  236. if (FLAGS_histogram) {
  237. fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
  238. }
  239. fflush(stdout);
  240. }
  241. };
  242. // State shared by all concurrent executions of the same benchmark.
  243. struct SharedState {
  244. port::Mutex mu;
  245. port::CondVar cv GUARDED_BY(mu);
  246. int total GUARDED_BY(mu);
  247. // Each thread goes through the following states:
  248. // (1) initializing
  249. // (2) waiting for others to be initialized
  250. // (3) running
  251. // (4) done
  252. int num_initialized GUARDED_BY(mu);
  253. int num_done GUARDED_BY(mu);
  254. bool start GUARDED_BY(mu);
  255. SharedState(int total)
  256. : cv(&mu), total(total), num_initialized(0), num_done(0), start(false) { }
  257. };
  258. // Per-thread state for concurrent executions of the same benchmark.
  259. struct ThreadState {
  260. int tid; // 0..n-1 when running in n threads
  261. Random rand; // Has different seeds for different threads
  262. Stats stats;
  263. SharedState* shared;
  264. ThreadState(int index)
  265. : tid(index),
  266. rand(1000 + index) {
  267. }
  268. };
  269. } // namespace
  270. class Benchmark {
  271. private:
  272. Cache* cache_;
  273. const FilterPolicy* filter_policy_;
  274. DB* db_;
  275. int num_;
  276. int value_size_;
  277. int entries_per_batch_;
  278. WriteOptions write_options_;
  279. int reads_;
  280. int heap_counter_;
  281. void PrintHeader() {
  282. const int kKeySize = 16;
  283. PrintEnvironment();
  284. fprintf(stdout, "Keys: %d bytes each\n", kKeySize);
  285. fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n",
  286. FLAGS_value_size,
  287. static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));
  288. fprintf(stdout, "Entries: %d\n", num_);
  289. fprintf(stdout, "RawSize: %.1f MB (estimated)\n",
  290. ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_)
  291. / 1048576.0));
  292. fprintf(stdout, "FileSize: %.1f MB (estimated)\n",
  293. (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_)
  294. / 1048576.0));
  295. PrintWarnings();
  296. fprintf(stdout, "------------------------------------------------\n");
  297. }
  298. void PrintWarnings() {
  299. #if defined(__GNUC__) && !defined(__OPTIMIZE__)
  300. fprintf(stdout,
  301. "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"
  302. );
  303. #endif
  304. #ifndef NDEBUG
  305. fprintf(stdout,
  306. "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
  307. #endif
  308. // See if snappy is working by attempting to compress a compressible string
  309. const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
  310. std::string compressed;
  311. if (!port::Snappy_Compress(text, sizeof(text), &compressed)) {
  312. fprintf(stdout, "WARNING: Snappy compression is not enabled\n");
  313. } else if (compressed.size() >= sizeof(text)) {
  314. fprintf(stdout, "WARNING: Snappy compression is not effective\n");
  315. }
  316. }
  317. void PrintEnvironment() {
  318. fprintf(stderr, "LevelDB: version %d.%d\n",
  319. kMajorVersion, kMinorVersion);
  320. #if defined(__linux)
  321. time_t now = time(nullptr);
  322. fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline
  323. FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
  324. if (cpuinfo != nullptr) {
  325. char line[1000];
  326. int num_cpus = 0;
  327. std::string cpu_type;
  328. std::string cache_size;
  329. while (fgets(line, sizeof(line), cpuinfo) != nullptr) {
  330. const char* sep = strchr(line, ':');
  331. if (sep == nullptr) {
  332. continue;
  333. }
  334. Slice key = TrimSpace(Slice(line, sep - 1 - line));
  335. Slice val = TrimSpace(Slice(sep + 1));
  336. if (key == "model name") {
  337. ++num_cpus;
  338. cpu_type = val.ToString();
  339. } else if (key == "cache size") {
  340. cache_size = val.ToString();
  341. }
  342. }
  343. fclose(cpuinfo);
  344. fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str());
  345. fprintf(stderr, "CPUCache: %s\n", cache_size.c_str());
  346. }
  347. #endif
  348. }
  349. public:
  350. Benchmark()
  351. : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : nullptr),
  352. filter_policy_(FLAGS_bloom_bits >= 0
  353. ? NewBloomFilterPolicy(FLAGS_bloom_bits)
  354. : nullptr),
  355. db_(nullptr),
  356. num_(FLAGS_num),
  357. value_size_(FLAGS_value_size),
  358. entries_per_batch_(1),
  359. reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
  360. heap_counter_(0) {
  361. std::vector<std::string> files;
  362. g_env->GetChildren(FLAGS_db, &files);
  363. for (size_t i = 0; i < files.size(); i++) {
  364. if (Slice(files[i]).starts_with("heap-")) {
  365. g_env->DeleteFile(std::string(FLAGS_db) + "/" + files[i]);
  366. }
  367. }
  368. if (!FLAGS_use_existing_db) {
  369. DestroyDB(FLAGS_db, Options());
  370. }
  371. }
  372. ~Benchmark() {
  373. delete db_;
  374. delete cache_;
  375. delete filter_policy_;
  376. }
  377. void Run() {
  378. PrintHeader();
  379. Open();
  380. const char* benchmarks = FLAGS_benchmarks;
  381. while (benchmarks != nullptr) {
  382. const char* sep = strchr(benchmarks, ',');
  383. Slice name;
  384. if (sep == nullptr) {
  385. name = benchmarks;
  386. benchmarks = nullptr;
  387. } else {
  388. name = Slice(benchmarks, sep - benchmarks);
  389. benchmarks = sep + 1;
  390. }
  391. // Reset parameters that may be overridden below
  392. num_ = FLAGS_num;
  393. reads_ = (FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads);
  394. value_size_ = FLAGS_value_size;
  395. entries_per_batch_ = 1;
  396. write_options_ = WriteOptions();
  397. void (Benchmark::*method)(ThreadState*) = nullptr;
  398. bool fresh_db = false;
  399. int num_threads = FLAGS_threads;
  400. if (name == Slice("open")) {
  401. method = &Benchmark::OpenBench;
  402. num_ /= 10000;
  403. if (num_ < 1) num_ = 1;
  404. } else if (name == Slice("fillseq")) {
  405. fresh_db = true;
  406. method = &Benchmark::WriteSeq;
  407. } else if (name == Slice("fillbatch")) {
  408. fresh_db = true;
  409. entries_per_batch_ = 1000;
  410. method = &Benchmark::WriteSeq;
  411. } else if (name == Slice("fillrandom")) {
  412. fresh_db = true;
  413. method = &Benchmark::WriteRandom;
  414. } else if (name == Slice("overwrite")) {
  415. fresh_db = false;
  416. method = &Benchmark::WriteRandom;
  417. } else if (name == Slice("fillsync")) {
  418. fresh_db = true;
  419. num_ /= 1000;
  420. write_options_.sync = true;
  421. method = &Benchmark::WriteRandom;
  422. } else if (name == Slice("fill100K")) {
  423. fresh_db = true;
  424. num_ /= 1000;
  425. value_size_ = 100 * 1000;
  426. method = &Benchmark::WriteRandom;
  427. } else if (name == Slice("readseq")) {
  428. method = &Benchmark::ReadSequential;
  429. } else if (name == Slice("readreverse")) {
  430. method = &Benchmark::ReadReverse;
  431. } else if (name == Slice("readrandom")) {
  432. method = &Benchmark::ReadRandom;
  433. } else if (name == Slice("readmissing")) {
  434. method = &Benchmark::ReadMissing;
  435. } else if (name == Slice("seekrandom")) {
  436. method = &Benchmark::SeekRandom;
  437. } else if (name == Slice("readhot")) {
  438. method = &Benchmark::ReadHot;
  439. } else if (name == Slice("readrandomsmall")) {
  440. reads_ /= 1000;
  441. method = &Benchmark::ReadRandom;
  442. } else if (name == Slice("deleteseq")) {
  443. method = &Benchmark::DeleteSeq;
  444. } else if (name == Slice("deleterandom")) {
  445. method = &Benchmark::DeleteRandom;
  446. } else if (name == Slice("readwhilewriting")) {
  447. num_threads++; // Add extra thread for writing
  448. method = &Benchmark::ReadWhileWriting;
  449. } else if (name == Slice("compact")) {
  450. method = &Benchmark::Compact;
  451. } else if (name == Slice("crc32c")) {
  452. method = &Benchmark::Crc32c;
  453. } else if (name == Slice("snappycomp")) {
  454. method = &Benchmark::SnappyCompress;
  455. } else if (name == Slice("snappyuncomp")) {
  456. method = &Benchmark::SnappyUncompress;
  457. } else if (name == Slice("heapprofile")) {
  458. HeapProfile();
  459. } else if (name == Slice("stats")) {
  460. PrintStats("leveldb.stats");
  461. } else if (name == Slice("sstables")) {
  462. PrintStats("leveldb.sstables");
  463. } else {
  464. if (name != Slice()) { // No error message for empty name
  465. fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
  466. }
  467. }
  468. if (fresh_db) {
  469. if (FLAGS_use_existing_db) {
  470. fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n",
  471. name.ToString().c_str());
  472. method = nullptr;
  473. } else {
  474. delete db_;
  475. db_ = nullptr;
  476. DestroyDB(FLAGS_db, Options());
  477. Open();
  478. }
  479. }
  480. if (method != nullptr) {
  481. RunBenchmark(num_threads, name, method);
  482. }
  483. }
  484. }
  485. private:
  486. struct ThreadArg {
  487. Benchmark* bm;
  488. SharedState* shared;
  489. ThreadState* thread;
  490. void (Benchmark::*method)(ThreadState*);
  491. };
  492. static void ThreadBody(void* v) {
  493. ThreadArg* arg = reinterpret_cast<ThreadArg*>(v);
  494. SharedState* shared = arg->shared;
  495. ThreadState* thread = arg->thread;
  496. {
  497. MutexLock l(&shared->mu);
  498. shared->num_initialized++;
  499. if (shared->num_initialized >= shared->total) {
  500. shared->cv.SignalAll();
  501. }
  502. while (!shared->start) {
  503. shared->cv.Wait();
  504. }
  505. }
  506. thread->stats.Start();
  507. (arg->bm->*(arg->method))(thread);
  508. thread->stats.Stop();
  509. {
  510. MutexLock l(&shared->mu);
  511. shared->num_done++;
  512. if (shared->num_done >= shared->total) {
  513. shared->cv.SignalAll();
  514. }
  515. }
  516. }
  517. void RunBenchmark(int n, Slice name,
  518. void (Benchmark::*method)(ThreadState*)) {
  519. SharedState shared(n);
  520. ThreadArg* arg = new ThreadArg[n];
  521. for (int i = 0; i < n; i++) {
  522. arg[i].bm = this;
  523. arg[i].method = method;
  524. arg[i].shared = &shared;
  525. arg[i].thread = new ThreadState(i);
  526. arg[i].thread->shared = &shared;
  527. g_env->StartThread(ThreadBody, &arg[i]);
  528. }
  529. shared.mu.Lock();
  530. while (shared.num_initialized < n) {
  531. shared.cv.Wait();
  532. }
  533. shared.start = true;
  534. shared.cv.SignalAll();
  535. while (shared.num_done < n) {
  536. shared.cv.Wait();
  537. }
  538. shared.mu.Unlock();
  539. for (int i = 1; i < n; i++) {
  540. arg[0].thread->stats.Merge(arg[i].thread->stats);
  541. }
  542. arg[0].thread->stats.Report(name);
  543. for (int i = 0; i < n; i++) {
  544. delete arg[i].thread;
  545. }
  546. delete[] arg;
  547. }
  548. void Crc32c(ThreadState* thread) {
  549. // Checksum about 500MB of data total
  550. const int size = 4096;
  551. const char* label = "(4K per op)";
  552. std::string data(size, 'x');
  553. int64_t bytes = 0;
  554. uint32_t crc = 0;
  555. while (bytes < 500 * 1048576) {
  556. crc = crc32c::Value(data.data(), size);
  557. thread->stats.FinishedSingleOp();
  558. bytes += size;
  559. }
  560. // Print so result is not dead
  561. fprintf(stderr, "... crc=0x%x\r", static_cast<unsigned int>(crc));
  562. thread->stats.AddBytes(bytes);
  563. thread->stats.AddMessage(label);
  564. }
  565. void SnappyCompress(ThreadState* thread) {
  566. RandomGenerator gen;
  567. Slice input = gen.Generate(Options().block_size);
  568. int64_t bytes = 0;
  569. int64_t produced = 0;
  570. bool ok = true;
  571. std::string compressed;
  572. while (ok && bytes < 1024 * 1048576) { // Compress 1G
  573. ok = port::Snappy_Compress(input.data(), input.size(), &compressed);
  574. produced += compressed.size();
  575. bytes += input.size();
  576. thread->stats.FinishedSingleOp();
  577. }
  578. if (!ok) {
  579. thread->stats.AddMessage("(snappy failure)");
  580. } else {
  581. char buf[100];
  582. snprintf(buf, sizeof(buf), "(output: %.1f%%)",
  583. (produced * 100.0) / bytes);
  584. thread->stats.AddMessage(buf);
  585. thread->stats.AddBytes(bytes);
  586. }
  587. }
  588. void SnappyUncompress(ThreadState* thread) {
  589. RandomGenerator gen;
  590. Slice input = gen.Generate(Options().block_size);
  591. std::string compressed;
  592. bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed);
  593. int64_t bytes = 0;
  594. char* uncompressed = new char[input.size()];
  595. while (ok && bytes < 1024 * 1048576) { // Compress 1G
  596. ok = port::Snappy_Uncompress(compressed.data(), compressed.size(),
  597. uncompressed);
  598. bytes += input.size();
  599. thread->stats.FinishedSingleOp();
  600. }
  601. delete[] uncompressed;
  602. if (!ok) {
  603. thread->stats.AddMessage("(snappy failure)");
  604. } else {
  605. thread->stats.AddBytes(bytes);
  606. }
  607. }
  608. void Open() {
  609. assert(db_ == nullptr);
  610. Options options;
  611. options.env = g_env;
  612. options.create_if_missing = !FLAGS_use_existing_db;
  613. options.block_cache = cache_;
  614. options.write_buffer_size = FLAGS_write_buffer_size;
  615. options.max_file_size = FLAGS_max_file_size;
  616. options.block_size = FLAGS_block_size;
  617. options.max_open_files = FLAGS_open_files;
  618. options.filter_policy = filter_policy_;
  619. options.reuse_logs = FLAGS_reuse_logs;
  620. Status s = DB::Open(options, FLAGS_db, &db_);
  621. if (!s.ok()) {
  622. fprintf(stderr, "open error: %s\n", s.ToString().c_str());
  623. exit(1);
  624. }
  625. }
  626. void OpenBench(ThreadState* thread) {
  627. for (int i = 0; i < num_; i++) {
  628. delete db_;
  629. Open();
  630. thread->stats.FinishedSingleOp();
  631. }
  632. }
  633. void WriteSeq(ThreadState* thread) {
  634. DoWrite(thread, true);
  635. }
  636. void WriteRandom(ThreadState* thread) {
  637. DoWrite(thread, false);
  638. }
  639. void DoWrite(ThreadState* thread, bool seq) {
  640. if (num_ != FLAGS_num) {
  641. char msg[100];
  642. snprintf(msg, sizeof(msg), "(%d ops)", num_);
  643. thread->stats.AddMessage(msg);
  644. }
  645. RandomGenerator gen;
  646. WriteBatch batch;
  647. Status s;
  648. int64_t bytes = 0;
  649. for (int i = 0; i < num_; i += entries_per_batch_) {
  650. batch.Clear();
  651. for (int j = 0; j < entries_per_batch_; j++) {
  652. const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num);
  653. char key[100];
  654. snprintf(key, sizeof(key), "%016d", k);
  655. batch.Put(key, gen.Generate(value_size_));
  656. bytes += value_size_ + strlen(key);
  657. thread->stats.FinishedSingleOp();
  658. }
  659. s = db_->Write(write_options_, &batch);
  660. if (!s.ok()) {
  661. fprintf(stderr, "put error: %s\n", s.ToString().c_str());
  662. exit(1);
  663. }
  664. }
  665. thread->stats.AddBytes(bytes);
  666. }
  667. void ReadSequential(ThreadState* thread) {
  668. Iterator* iter = db_->NewIterator(ReadOptions());
  669. int i = 0;
  670. int64_t bytes = 0;
  671. for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) {
  672. bytes += iter->key().size() + iter->value().size();
  673. thread->stats.FinishedSingleOp();
  674. ++i;
  675. }
  676. delete iter;
  677. thread->stats.AddBytes(bytes);
  678. }
  679. void ReadReverse(ThreadState* thread) {
  680. Iterator* iter = db_->NewIterator(ReadOptions());
  681. int i = 0;
  682. int64_t bytes = 0;
  683. for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) {
  684. bytes += iter->key().size() + iter->value().size();
  685. thread->stats.FinishedSingleOp();
  686. ++i;
  687. }
  688. delete iter;
  689. thread->stats.AddBytes(bytes);
  690. }
  691. void ReadRandom(ThreadState* thread) {
  692. ReadOptions options;
  693. std::string value;
  694. int found = 0;
  695. for (int i = 0; i < reads_; i++) {
  696. char key[100];
  697. const int k = thread->rand.Next() % FLAGS_num;
  698. snprintf(key, sizeof(key), "%016d", k);
  699. if (db_->Get(options, key, &value).ok()) {
  700. found++;
  701. }
  702. thread->stats.FinishedSingleOp();
  703. }
  704. char msg[100];
  705. snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_);
  706. thread->stats.AddMessage(msg);
  707. }
  708. void ReadMissing(ThreadState* thread) {
  709. ReadOptions options;
  710. std::string value;
  711. for (int i = 0; i < reads_; i++) {
  712. char key[100];
  713. const int k = thread->rand.Next() % FLAGS_num;
  714. snprintf(key, sizeof(key), "%016d.", k);
  715. db_->Get(options, key, &value);
  716. thread->stats.FinishedSingleOp();
  717. }
  718. }
  719. void ReadHot(ThreadState* thread) {
  720. ReadOptions options;
  721. std::string value;
  722. const int range = (FLAGS_num + 99) / 100;
  723. for (int i = 0; i < reads_; i++) {
  724. char key[100];
  725. const int k = thread->rand.Next() % range;
  726. snprintf(key, sizeof(key), "%016d", k);
  727. db_->Get(options, key, &value);
  728. thread->stats.FinishedSingleOp();
  729. }
  730. }
  731. void SeekRandom(ThreadState* thread) {
  732. ReadOptions options;
  733. int found = 0;
  734. for (int i = 0; i < reads_; i++) {
  735. Iterator* iter = db_->NewIterator(options);
  736. char key[100];
  737. const int k = thread->rand.Next() % FLAGS_num;
  738. snprintf(key, sizeof(key), "%016d", k);
  739. iter->Seek(key);
  740. if (iter->Valid() && iter->key() == key) found++;
  741. delete iter;
  742. thread->stats.FinishedSingleOp();
  743. }
  744. char msg[100];
  745. snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_);
  746. thread->stats.AddMessage(msg);
  747. }
  748. void DoDelete(ThreadState* thread, bool seq) {
  749. RandomGenerator gen;
  750. WriteBatch batch;
  751. Status s;
  752. for (int i = 0; i < num_; i += entries_per_batch_) {
  753. batch.Clear();
  754. for (int j = 0; j < entries_per_batch_; j++) {
  755. const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num);
  756. char key[100];
  757. snprintf(key, sizeof(key), "%016d", k);
  758. batch.Delete(key);
  759. thread->stats.FinishedSingleOp();
  760. }
  761. s = db_->Write(write_options_, &batch);
  762. if (!s.ok()) {
  763. fprintf(stderr, "del error: %s\n", s.ToString().c_str());
  764. exit(1);
  765. }
  766. }
  767. }
  768. void DeleteSeq(ThreadState* thread) {
  769. DoDelete(thread, true);
  770. }
  771. void DeleteRandom(ThreadState* thread) {
  772. DoDelete(thread, false);
  773. }
  774. void ReadWhileWriting(ThreadState* thread) {
  775. if (thread->tid > 0) {
  776. ReadRandom(thread);
  777. } else {
  778. // Special thread that keeps writing until other threads are done.
  779. RandomGenerator gen;
  780. while (true) {
  781. {
  782. MutexLock l(&thread->shared->mu);
  783. if (thread->shared->num_done + 1 >= thread->shared->num_initialized) {
  784. // Other threads have finished
  785. break;
  786. }
  787. }
  788. const int k = thread->rand.Next() % FLAGS_num;
  789. char key[100];
  790. snprintf(key, sizeof(key), "%016d", k);
  791. Status s = db_->Put(write_options_, key, gen.Generate(value_size_));
  792. if (!s.ok()) {
  793. fprintf(stderr, "put error: %s\n", s.ToString().c_str());
  794. exit(1);
  795. }
  796. }
  797. // Do not count any of the preceding work/delay in stats.
  798. thread->stats.Start();
  799. }
  800. }
  801. void Compact(ThreadState* thread) {
  802. db_->CompactRange(nullptr, nullptr);
  803. }
  804. void PrintStats(const char* key) {
  805. std::string stats;
  806. if (!db_->GetProperty(key, &stats)) {
  807. stats = "(failed)";
  808. }
  809. fprintf(stdout, "\n%s\n", stats.c_str());
  810. }
  811. static void WriteToFile(void* arg, const char* buf, int n) {
  812. reinterpret_cast<WritableFile*>(arg)->Append(Slice(buf, n));
  813. }
  814. void HeapProfile() {
  815. char fname[100];
  816. snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_);
  817. WritableFile* file;
  818. Status s = g_env->NewWritableFile(fname, &file);
  819. if (!s.ok()) {
  820. fprintf(stderr, "%s\n", s.ToString().c_str());
  821. return;
  822. }
  823. bool ok = port::GetHeapProfile(WriteToFile, file);
  824. delete file;
  825. if (!ok) {
  826. fprintf(stderr, "heap profiling not supported\n");
  827. g_env->DeleteFile(fname);
  828. }
  829. }
  830. };
  831. } // namespace leveldb
  832. int main(int argc, char** argv) {
  833. FLAGS_write_buffer_size = leveldb::Options().write_buffer_size;
  834. FLAGS_max_file_size = leveldb::Options().max_file_size;
  835. FLAGS_block_size = leveldb::Options().block_size;
  836. FLAGS_open_files = leveldb::Options().max_open_files;
  837. std::string default_db_path;
  838. for (int i = 1; i < argc; i++) {
  839. double d;
  840. int n;
  841. char junk;
  842. if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
  843. FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
  844. } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
  845. FLAGS_compression_ratio = d;
  846. } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
  847. (n == 0 || n == 1)) {
  848. FLAGS_histogram = n;
  849. } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 &&
  850. (n == 0 || n == 1)) {
  851. FLAGS_use_existing_db = n;
  852. } else if (sscanf(argv[i], "--reuse_logs=%d%c", &n, &junk) == 1 &&
  853. (n == 0 || n == 1)) {
  854. FLAGS_reuse_logs = n;
  855. } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
  856. FLAGS_num = n;
  857. } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
  858. FLAGS_reads = n;
  859. } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) {
  860. FLAGS_threads = n;
  861. } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
  862. FLAGS_value_size = n;
  863. } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) {
  864. FLAGS_write_buffer_size = n;
  865. } else if (sscanf(argv[i], "--max_file_size=%d%c", &n, &junk) == 1) {
  866. FLAGS_max_file_size = n;
  867. } else if (sscanf(argv[i], "--block_size=%d%c", &n, &junk) == 1) {
  868. FLAGS_block_size = n;
  869. } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) {
  870. FLAGS_cache_size = n;
  871. } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) {
  872. FLAGS_bloom_bits = n;
  873. } else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) {
  874. FLAGS_open_files = n;
  875. } else if (strncmp(argv[i], "--db=", 5) == 0) {
  876. FLAGS_db = argv[i] + 5;
  877. } else {
  878. fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
  879. exit(1);
  880. }
  881. }
  882. leveldb::g_env = leveldb::Env::Default();
  883. // Choose a location for the test database if none given with --db=<path>
  884. if (FLAGS_db == nullptr) {
  885. leveldb::g_env->GetTestDirectory(&default_db_path);
  886. default_db_path += "/dbbench";
  887. FLAGS_db = default_db_path.c_str();
  888. }
  889. leveldb::Benchmark benchmark;
  890. benchmark.Run();
  891. return 0;
  892. }