summaryrefslogtreecommitdiff
path: root/db/db_bench.cc
diff options
context:
space:
mode:
authorjorlow@chromium.org <jorlow@chromium.org@62dab493-f737-651d-591e-8d6aee1b9529>2011-03-18 22:37:00 +0000
committerjorlow@chromium.org <jorlow@chromium.org@62dab493-f737-651d-591e-8d6aee1b9529>2011-03-18 22:37:00 +0000
commitf67e15e50f392625b4097caf22e8be1b0fe96013 (patch)
tree1cb1764c7627f9bac27ed0e0abf27010156e5007 /db/db_bench.cc
parent54f1fd7eef101db1dfb2bb66a59083c45a38aa4a (diff)
downloadleveldb-f67e15e50f392625b4097caf22e8be1b0fe96013.tar.gz
Initial checkin.
git-svn-id: https://leveldb.googlecode.com/svn/trunk@2 62dab493-f737-651d-591e-8d6aee1b9529
Diffstat (limited to 'db/db_bench.cc')
-rw-r--r--db/db_bench.cc376
1 files changed, 376 insertions, 0 deletions
diff --git a/db/db_bench.cc b/db/db_bench.cc
new file mode 100644
index 0000000..4ccdd5a
--- /dev/null
+++ b/db/db_bench.cc
@@ -0,0 +1,376 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "db/db_impl.h"
+#include "db/version_set.h"
+#include "include/cache.h"
+#include "include/db.h"
+#include "include/env.h"
+#include "include/write_batch.h"
+#include "util/histogram.h"
+#include "util/random.h"
+#include "util/testutil.h"
+
+// Comma-separated list of operations to run in the specified order
+// Actual benchmarks:
+// writeseq -- write N values in sequential key order
+// writerandom -- write N values in random key order
+// writebig -- write N/1000 100K valuesin random order
+// readseq -- read N values sequentially
+// readrandom -- read N values in random order
+// Meta operations:
+// compact -- Compact the entire DB
+// heapprofile -- Dump a heap profile (if supported by this port)
+// sync -- switch to synchronous writes (not the default)
+// nosync -- switch to asynchronous writes (the default)
+// tenth -- divide N by 10 (i.e., following benchmarks are smaller)
+// normal -- reset N back to its normal value (1000000)
+static const char* FLAGS_benchmarks =
+ "writeseq,"
+ "writeseq,"
+ "writerandom,"
+ "sync,tenth,tenth,writerandom,nosync,normal,"
+ "readseq,"
+ "readrandom,"
+ "compact,"
+ "readseq,"
+ "readrandom,"
+ "writebig";
+
+// Number of key/values to place in database
+static int FLAGS_num = 1000000;
+
+// Size of each value
+static int FLAGS_value_size = 100;
+
+// Arrange to generate values that shrink to this fraction of
+// their original size after compression
+static double FLAGS_compression_ratio = 0.25;
+
+// Print histogram of operation timings
+static bool FLAGS_histogram = false;
+
+// Number of bytes to buffer in memtable before compacting
+static int FLAGS_write_buffer_size = 1 << 20;
+
+namespace leveldb {
+
+// Helper for quickly generating random data.
+namespace {
+class RandomGenerator {
+ private:
+ std::string data_;
+ int pos_;
+
+ public:
+ RandomGenerator() {
+ // We use a limited amount of data over and over again and ensure
+ // that it is larger than the compression window (32KB), and also
+ // large enough to serve all typical value sizes we want to write.
+ Random rnd(301);
+ std::string piece;
+ while (data_.size() < 1048576) {
+ // Add a short fragment that is as compressible as specified
+ // by FLAGS_compression_ratio.
+ test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
+ data_.append(piece);
+ }
+ pos_ = 0;
+ }
+
+ Slice Generate(int len) {
+ if (pos_ + len > data_.size()) {
+ pos_ = 0;
+ assert(len < data_.size());
+ }
+ pos_ += len;
+ return Slice(data_.data() + pos_ - len, len);
+ }
+};
+}
+
+class Benchmark {
+ private:
+ Cache* cache_;
+ DB* db_;
+ int num_;
+ bool sync_;
+ int heap_counter_;
+ double start_;
+ double last_op_finish_;
+ int64_t bytes_;
+ std::string message_;
+ Histogram hist_;
+ RandomGenerator gen_;
+ Random rand_;
+
+ // State kept for progress messages
+ int done_;
+ int next_report_; // When to report next
+
+ void Start() {
+ start_ = Env::Default()->NowMicros() * 1e-6;
+ bytes_ = 0;
+ message_.clear();
+ last_op_finish_ = start_;
+ hist_.Clear();
+ done_ = 0;
+ next_report_ = 100;
+ }
+
+ void FinishedSingleOp() {
+ if (FLAGS_histogram) {
+ double now = Env::Default()->NowMicros() * 1e-6;
+ double micros = (now - last_op_finish_) * 1e6;
+ hist_.Add(micros);
+ if (micros > 20000) {
+ fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
+ fflush(stderr);
+ }
+ last_op_finish_ = now;
+ }
+
+ done_++;
+ if (done_ >= next_report_) {
+ if (next_report_ < 1000) {
+ next_report_ += 100;
+ } else if (next_report_ < 10000) {
+ next_report_ += 1000;
+ } else if (next_report_ < 100000) {
+ next_report_ += 10000;
+ } else {
+ next_report_ += 100000;
+ }
+ fprintf(stderr, "... finished %d ops%30s\r", done_, "");
+ fflush(stderr);
+ }
+ }
+
+ void Stop(const Slice& name) {
+ double finish = Env::Default()->NowMicros() * 1e-6;
+
+ // Pretend at least one op was done in case we are running a benchmark
+ // that does nto call FinishedSingleOp().
+ if (done_ < 1) done_ = 1;
+
+ if (bytes_ > 0) {
+ char rate[100];
+ snprintf(rate, sizeof(rate), "%5.1f MB/s",
+ (bytes_ / 1048576.0) / (finish - start_));
+ if (!message_.empty()) {
+ message_.push_back(' ');
+ }
+ message_.append(rate);
+ }
+
+ fprintf(stdout, "%-12s : %10.3f micros/op;%s%s\n",
+ name.ToString().c_str(),
+ (finish - start_) * 1e6 / done_,
+ (message_.empty() ? "" : " "),
+ message_.c_str());
+ if (FLAGS_histogram) {
+ fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
+ }
+ fflush(stdout);
+ }
+
+ public:
+ enum Order { SEQUENTIAL, RANDOM };
+
+ Benchmark() : cache_(NewLRUCache(200<<20)),
+ db_(NULL),
+ num_(FLAGS_num),
+ sync_(false),
+ heap_counter_(0),
+ bytes_(0),
+ rand_(301) {
+ std::vector<std::string> files;
+ Env::Default()->GetChildren("/tmp/dbbench", &files);
+ for (int i = 0; i < files.size(); i++) {
+ if (Slice(files[i]).starts_with("heap-")) {
+ Env::Default()->DeleteFile("/tmp/dbbench/" + files[i]);
+ }
+ }
+ DestroyDB("/tmp/dbbench", Options());
+ }
+
+ ~Benchmark() {
+ delete db_;
+ delete cache_;
+ }
+
+ void Run() {
+ Options options;
+ options.create_if_missing = true;
+ options.max_open_files = 10000;
+ options.block_cache = cache_;
+ options.write_buffer_size = FLAGS_write_buffer_size;
+
+ Start();
+ Status s = DB::Open(options, "/tmp/dbbench", &db_);
+ Stop("open");
+ if (!s.ok()) {
+ fprintf(stderr, "open error: %s\n", s.ToString().c_str());
+ exit(1);
+ }
+
+ const char* benchmarks = FLAGS_benchmarks;
+ while (benchmarks != NULL) {
+ const char* sep = strchr(benchmarks, ',');
+ Slice name;
+ if (sep == NULL) {
+ name = benchmarks;
+ benchmarks = NULL;
+ } else {
+ name = Slice(benchmarks, sep - benchmarks);
+ benchmarks = sep + 1;
+ }
+
+ Start();
+ if (name == Slice("writeseq")) {
+ Write(SEQUENTIAL, num_, FLAGS_value_size);
+ } else if (name == Slice("writerandom")) {
+ Write(RANDOM, num_, FLAGS_value_size);
+ } else if (name == Slice("writebig")) {
+ Write(RANDOM, num_ / 1000, 100 * 1000);
+ } else if (name == Slice("readseq")) {
+ Read(SEQUENTIAL);
+ } else if (name == Slice("readrandom")) {
+ Read(RANDOM);
+ } else if (name == Slice("compact")) {
+ Compact();
+ } else if (name == Slice("heapprofile")) {
+ HeapProfile();
+ } else if (name == Slice("sync")) {
+ sync_ = true;
+ } else if (name == Slice("nosync")) {
+ sync_ = false;
+ } else if (name == Slice("tenth")) {
+ num_ = num_ / 10;
+ } else if (name == Slice("normal")) {
+ num_ = FLAGS_num;
+ } else {
+ fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
+ }
+ Stop(name);
+ }
+ }
+
+ void Write(Order order, int num_entries, int value_size) {
+ WriteBatch batch;
+ Status s;
+ std::string val;
+ WriteOptions options;
+ options.sync = sync_;
+ for (int i = 0; i < num_entries; i++) {
+ const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % FLAGS_num);
+ char key[100];
+ snprintf(key, sizeof(key), "%012d", k);
+ batch.Clear();
+ batch.Put(key, gen_.Generate(value_size));
+ s = db_->Write(options, &batch);
+ bytes_ += value_size + strlen(key);
+ if (!s.ok()) {
+ fprintf(stderr, "put error: %s\n", s.ToString().c_str());
+ exit(1);
+ }
+ FinishedSingleOp();
+ }
+ }
+
+ void Read(Order order) {
+ ReadOptions options;
+ if (order == SEQUENTIAL) {
+ Iterator* iter = db_->NewIterator(options);
+ int i = 0;
+ for (iter->SeekToFirst(); i < num_ && iter->Valid(); iter->Next()) {
+ bytes_ += iter->key().size() + iter->value().size();
+ FinishedSingleOp();
+ ++i;
+ }
+ delete iter;
+ } else {
+ std::string value;
+ for (int i = 0; i < num_; i++) {
+ char key[100];
+ const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % FLAGS_num);
+ snprintf(key, sizeof(key), "%012d", k);
+ db_->Get(options, key, &value);
+ FinishedSingleOp();
+ }
+ }
+ }
+
+ void Compact() {
+ DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
+ dbi->TEST_CompactMemTable();
+ int max_level_with_files = 1;
+ for (int level = 1; level < config::kNumLevels; level++) {
+ uint64_t v;
+ char name[100];
+ snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level);
+ if (db_->GetProperty(name, &v) && v > 0) {
+ max_level_with_files = level;
+ }
+ }
+ for (int level = 0; level < max_level_with_files; level++) {
+ dbi->TEST_CompactRange(level, "", "~");
+ }
+ }
+
+ static void WriteToFile(void* arg, const char* buf, int n) {
+ reinterpret_cast<WritableFile*>(arg)->Append(Slice(buf, n));
+ }
+
+ void HeapProfile() {
+ char fname[100];
+ snprintf(fname, sizeof(fname), "/tmp/dbbench/heap-%04d", ++heap_counter_);
+ WritableFile* file;
+ Status s = Env::Default()->NewWritableFile(fname, &file);
+ if (!s.ok()) {
+ message_ = s.ToString();
+ return;
+ }
+ bool ok = port::GetHeapProfile(WriteToFile, file);
+ delete file;
+ if (!ok) {
+ message_ = "not supported";
+ Env::Default()->DeleteFile(fname);
+ }
+ }
+};
+
+}
+
+int main(int argc, char** argv) {
+ for (int i = 1; i < argc; i++) {
+ double d;
+ int n;
+ char junk;
+ if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
+ FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
+ } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
+ FLAGS_compression_ratio = d;
+ } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
+ (n == 0 || n == 1)) {
+ FLAGS_histogram = n;
+ } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
+ FLAGS_num = n;
+ } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
+ FLAGS_value_size = n;
+ } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) {
+ FLAGS_write_buffer_size = n;
+ } else {
+ fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
+ exit(1);
+ }
+ }
+
+ leveldb::Benchmark benchmark;
+ benchmark.Run();
+ return 0;
+}