db_bench.cc raw

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