~ubuntu-branches/ubuntu/quantal/ceph/quantal

« back to all changes in this revision

Viewing changes to src/leveldb/doc/bench/db_bench_tree_db.cc

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2012-07-16 09:56:24 UTC
  • mfrom: (0.3.11)
  • mto: This revision was merged to the branch mainline in revision 17.
  • Revision ID: package-import@ubuntu.com-20120716095624-azr2w4hbhei1rxmx
Tags: upstream-0.48
ImportĀ upstreamĀ versionĀ 0.48

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 <kcpolydb.h>
8
 
#include "util/histogram.h"
9
 
#include "util/random.h"
10
 
#include "util/testutil.h"
11
 
 
12
 
// Comma-separated list of operations to run in the specified order
13
 
//   Actual benchmarks:
14
 
//
15
 
//   fillseq       -- write N values in sequential key order in async mode
16
 
//   fillrandom    -- write N values in random key order in async mode
17
 
//   overwrite     -- overwrite N values in random key order in async mode
18
 
//   fillseqsync   -- write N/100 values in sequential key order in sync mode
19
 
//   fillrandsync  -- write N/100 values in random key order in sync mode
20
 
//   fillrand100K  -- write N/1000 100K values in random order in async mode
21
 
//   fillseq100K   -- write N/1000 100K values in seq order in async mode
22
 
//   readseq       -- read N times sequentially
23
 
//   readseq100K   -- read N/1000 100K values in sequential order in async mode
24
 
//   readrand100K  -- read N/1000 100K values in sequential order in async mode
25
 
//   readrandom    -- read N times in random order
26
 
static const char* FLAGS_benchmarks =
27
 
    "fillseq,"
28
 
    "fillseqsync,"
29
 
    "fillrandsync,"
30
 
    "fillrandom,"
31
 
    "overwrite,"
32
 
    "readrandom,"
33
 
    "readseq,"
34
 
    "fillrand100K,"
35
 
    "fillseq100K,"
36
 
    "readseq100K,"
37
 
    "readrand100K,"
38
 
    ;
39
 
 
40
 
// Number of key/values to place in database
41
 
static int FLAGS_num = 1000000;
42
 
 
43
 
// Number of read operations to do.  If negative, do FLAGS_num reads.
44
 
static int FLAGS_reads = -1;
45
 
 
46
 
// Size of each value
47
 
static int FLAGS_value_size = 100;
48
 
 
49
 
// Arrange to generate values that shrink to this fraction of
50
 
// their original size after compression
51
 
static double FLAGS_compression_ratio = 0.5;
52
 
 
53
 
// Print histogram of operation timings
54
 
static bool FLAGS_histogram = false;
55
 
 
56
 
// Cache size. Default 4 MB
57
 
static int FLAGS_cache_size = 4194304;
58
 
 
59
 
// Page size. Default 1 KB
60
 
static int FLAGS_page_size = 1024;
61
 
 
62
 
// If true, do not destroy the existing database.  If you set this
63
 
// flag and also specify a benchmark that wants a fresh database, that
64
 
// benchmark will fail.
65
 
static bool FLAGS_use_existing_db = false;
66
 
 
67
 
// Compression flag. If true, compression is on. If false, compression
68
 
// is off.
69
 
static bool FLAGS_compression = true;
70
 
 
71
 
inline
72
 
static void DBSynchronize(kyotocabinet::TreeDB* db_)
73
 
{
74
 
  // Synchronize will flush writes to disk
75
 
  if (!db_->synchronize()) {
76
 
    fprintf(stderr, "synchronize error: %s\n", db_->error().name());
77
 
  }
78
 
}
79
 
 
80
 
namespace leveldb {
81
 
 
82
 
// Helper for quickly generating random data.
83
 
namespace {
84
 
class RandomGenerator {
85
 
 private:
86
 
  std::string data_;
87
 
  int pos_;
88
 
 
89
 
 public:
90
 
  RandomGenerator() {
91
 
    // We use a limited amount of data over and over again and ensure
92
 
    // that it is larger than the compression window (32KB), and also
93
 
    // large enough to serve all typical value sizes we want to write.
94
 
    Random rnd(301);
95
 
    std::string piece;
96
 
    while (data_.size() < 1048576) {
97
 
      // Add a short fragment that is as compressible as specified
98
 
      // by FLAGS_compression_ratio.
99
 
      test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
100
 
      data_.append(piece);
101
 
    }
102
 
    pos_ = 0;
103
 
  }
104
 
 
105
 
  Slice Generate(int len) {
106
 
    if (pos_ + len > data_.size()) {
107
 
      pos_ = 0;
108
 
      assert(len < data_.size());
109
 
    }
110
 
    pos_ += len;
111
 
    return Slice(data_.data() + pos_ - len, len);
112
 
  }
113
 
};
114
 
 
115
 
static Slice TrimSpace(Slice s) {
116
 
  int start = 0;
117
 
  while (start < s.size() && isspace(s[start])) {
118
 
    start++;
119
 
  }
120
 
  int limit = s.size();
121
 
  while (limit > start && isspace(s[limit-1])) {
122
 
    limit--;
123
 
  }
124
 
  return Slice(s.data() + start, limit - start);
125
 
}
126
 
 
127
 
}  // namespace
128
 
 
129
 
class Benchmark {
130
 
 private:
131
 
  kyotocabinet::TreeDB* db_;
132
 
  int db_num_;
133
 
  int num_;
134
 
  int reads_;
135
 
  double start_;
136
 
  double last_op_finish_;
137
 
  int64_t bytes_;
138
 
  std::string message_;
139
 
  Histogram hist_;
140
 
  RandomGenerator gen_;
141
 
  Random rand_;
142
 
  kyotocabinet::LZOCompressor<kyotocabinet::LZO::RAW> comp_;
143
 
 
144
 
  // State kept for progress messages
145
 
  int done_;
146
 
  int next_report_;     // When to report next
147
 
 
148
 
  void PrintHeader() {
149
 
    const int kKeySize = 16;
150
 
    PrintEnvironment();
151
 
    fprintf(stdout, "Keys:       %d bytes each\n", kKeySize);
152
 
    fprintf(stdout, "Values:     %d bytes each (%d bytes after compression)\n",
153
 
            FLAGS_value_size,
154
 
            static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));
155
 
    fprintf(stdout, "Entries:    %d\n", num_);
156
 
    fprintf(stdout, "RawSize:    %.1f MB (estimated)\n",
157
 
            ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_)
158
 
             / 1048576.0));
159
 
    fprintf(stdout, "FileSize:   %.1f MB (estimated)\n",
160
 
            (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_)
161
 
             / 1048576.0));
162
 
    PrintWarnings();
163
 
    fprintf(stdout, "------------------------------------------------\n");
164
 
  }
165
 
 
166
 
  void PrintWarnings() {
167
 
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
168
 
    fprintf(stdout,
169
 
            "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"
170
 
            );
171
 
#endif
172
 
#ifndef NDEBUG
173
 
    fprintf(stdout,
174
 
            "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
175
 
#endif
176
 
  }
177
 
 
178
 
  void PrintEnvironment() {
179
 
    fprintf(stderr, "Kyoto Cabinet:    version %s, lib ver %d, lib rev %d\n",
180
 
            kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV);
181
 
 
182
 
#if defined(__linux)
183
 
    time_t now = time(NULL);
184
 
    fprintf(stderr, "Date:           %s", ctime(&now));  // ctime() adds newline
185
 
 
186
 
    FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
187
 
    if (cpuinfo != NULL) {
188
 
      char line[1000];
189
 
      int num_cpus = 0;
190
 
      std::string cpu_type;
191
 
      std::string cache_size;
192
 
      while (fgets(line, sizeof(line), cpuinfo) != NULL) {
193
 
        const char* sep = strchr(line, ':');
194
 
        if (sep == NULL) {
195
 
          continue;
196
 
        }
197
 
        Slice key = TrimSpace(Slice(line, sep - 1 - line));
198
 
        Slice val = TrimSpace(Slice(sep + 1));
199
 
        if (key == "model name") {
200
 
          ++num_cpus;
201
 
          cpu_type = val.ToString();
202
 
        } else if (key == "cache size") {
203
 
          cache_size = val.ToString();
204
 
        }
205
 
      }
206
 
      fclose(cpuinfo);
207
 
      fprintf(stderr, "CPU:            %d * %s\n", num_cpus, cpu_type.c_str());
208
 
      fprintf(stderr, "CPUCache:       %s\n", cache_size.c_str());
209
 
    }
210
 
#endif
211
 
  }
212
 
 
213
 
  void Start() {
214
 
    start_ = Env::Default()->NowMicros() * 1e-6;
215
 
    bytes_ = 0;
216
 
    message_.clear();
217
 
    last_op_finish_ = start_;
218
 
    hist_.Clear();
219
 
    done_ = 0;
220
 
    next_report_ = 100;
221
 
  }
222
 
 
223
 
  void FinishedSingleOp() {
224
 
    if (FLAGS_histogram) {
225
 
      double now = Env::Default()->NowMicros() * 1e-6;
226
 
      double micros = (now - last_op_finish_) * 1e6;
227
 
      hist_.Add(micros);
228
 
      if (micros > 20000) {
229
 
        fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
230
 
        fflush(stderr);
231
 
      }
232
 
      last_op_finish_ = now;
233
 
    }
234
 
 
235
 
    done_++;
236
 
    if (done_ >= next_report_) {
237
 
      if      (next_report_ < 1000)   next_report_ += 100;
238
 
      else if (next_report_ < 5000)   next_report_ += 500;
239
 
      else if (next_report_ < 10000)  next_report_ += 1000;
240
 
      else if (next_report_ < 50000)  next_report_ += 5000;
241
 
      else if (next_report_ < 100000) next_report_ += 10000;
242
 
      else if (next_report_ < 500000) next_report_ += 50000;
243
 
      else                            next_report_ += 100000;
244
 
      fprintf(stderr, "... finished %d ops%30s\r", done_, "");
245
 
      fflush(stderr);
246
 
    }
247
 
  }
248
 
 
249
 
  void Stop(const Slice& name) {
250
 
    double finish = Env::Default()->NowMicros() * 1e-6;
251
 
 
252
 
    // Pretend at least one op was done in case we are running a benchmark
253
 
    // that does not call FinishedSingleOp().
254
 
    if (done_ < 1) done_ = 1;
255
 
 
256
 
    if (bytes_ > 0) {
257
 
      char rate[100];
258
 
      snprintf(rate, sizeof(rate), "%6.1f MB/s",
259
 
               (bytes_ / 1048576.0) / (finish - start_));
260
 
      if (!message_.empty()) {
261
 
        message_  = std::string(rate) + " " + message_;
262
 
      } else {
263
 
        message_ = rate;
264
 
      }
265
 
    }
266
 
 
267
 
    fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
268
 
            name.ToString().c_str(),
269
 
            (finish - start_) * 1e6 / done_,
270
 
            (message_.empty() ? "" : " "),
271
 
            message_.c_str());
272
 
    if (FLAGS_histogram) {
273
 
      fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
274
 
    }
275
 
    fflush(stdout);
276
 
  }
277
 
 
278
 
 public:
279
 
  enum Order {
280
 
    SEQUENTIAL,
281
 
    RANDOM
282
 
  };
283
 
  enum DBState {
284
 
    FRESH,
285
 
    EXISTING
286
 
  };
287
 
 
288
 
  Benchmark()
289
 
  : db_(NULL),
290
 
    num_(FLAGS_num),
291
 
    reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
292
 
    bytes_(0),
293
 
    rand_(301) {
294
 
    std::vector<std::string> files;
295
 
    Env::Default()->GetChildren("/tmp", &files);
296
 
    if (!FLAGS_use_existing_db) {
297
 
      for (int i = 0; i < files.size(); i++) {
298
 
        if (Slice(files[i]).starts_with("dbbench_polyDB")) {
299
 
          Env::Default()->DeleteFile("/tmp/" + files[i]);
300
 
        }
301
 
      }
302
 
    }
303
 
  }
304
 
 
305
 
  ~Benchmark() {
306
 
    if (!db_->close()) {
307
 
      fprintf(stderr, "close error: %s\n", db_->error().name());
308
 
    }
309
 
  }
310
 
 
311
 
  void Run() {
312
 
    PrintHeader();
313
 
    Open(false);
314
 
 
315
 
    const char* benchmarks = FLAGS_benchmarks;
316
 
    while (benchmarks != NULL) {
317
 
      const char* sep = strchr(benchmarks, ',');
318
 
      Slice name;
319
 
      if (sep == NULL) {
320
 
        name = benchmarks;
321
 
        benchmarks = NULL;
322
 
      } else {
323
 
        name = Slice(benchmarks, sep - benchmarks);
324
 
        benchmarks = sep + 1;
325
 
      }
326
 
 
327
 
      Start();
328
 
 
329
 
      bool known = true;
330
 
      bool write_sync = false;
331
 
      if (name == Slice("fillseq")) {
332
 
        Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1);
333
 
        
334
 
      } else if (name == Slice("fillrandom")) {
335
 
        Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1);
336
 
        DBSynchronize(db_);
337
 
      } else if (name == Slice("overwrite")) {
338
 
        Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1);
339
 
        DBSynchronize(db_);
340
 
      } else if (name == Slice("fillrandsync")) {
341
 
        write_sync = true;
342
 
        Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1);
343
 
        DBSynchronize(db_);
344
 
      } else if (name == Slice("fillseqsync")) {
345
 
        write_sync = true;
346
 
        Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1);
347
 
        DBSynchronize(db_);
348
 
      } else if (name == Slice("fillrand100K")) {
349
 
        Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1);
350
 
        DBSynchronize(db_);
351
 
      } else if (name == Slice("fillseq100K")) {
352
 
        Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1);
353
 
        DBSynchronize(db_);
354
 
      } else if (name == Slice("readseq")) {
355
 
        ReadSequential();
356
 
      } else if (name == Slice("readrandom")) {
357
 
        ReadRandom();
358
 
      } else if (name == Slice("readrand100K")) {
359
 
        int n = reads_;
360
 
        reads_ /= 1000;
361
 
        ReadRandom();
362
 
        reads_ = n;
363
 
      } else if (name == Slice("readseq100K")) {
364
 
        int n = reads_;
365
 
        reads_ /= 1000;
366
 
        ReadSequential();
367
 
        reads_ = n;
368
 
      } else {
369
 
        known = false;
370
 
        if (name != Slice()) {  // No error message for empty name
371
 
          fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
372
 
        }
373
 
      }
374
 
      if (known) {
375
 
        Stop(name);
376
 
      }
377
 
    }
378
 
  }
379
 
 
380
 
 private:
381
 
    void Open(bool sync) {
382
 
    assert(db_ == NULL);
383
 
 
384
 
    // Initialize db_
385
 
    db_ = new kyotocabinet::TreeDB();
386
 
    char file_name[100];
387
 
    db_num_++;
388
 
    snprintf(file_name, sizeof(file_name), "/tmp/dbbench_polyDB-%d.kct",
389
 
                               db_num_);
390
 
 
391
 
    // Create tuning options and open the database
392
 
    int open_options = kyotocabinet::PolyDB::OWRITER |
393
 
                       kyotocabinet::PolyDB::OCREATE;
394
 
    int tune_options = kyotocabinet::TreeDB::TSMALL |
395
 
        kyotocabinet::TreeDB::TLINEAR;
396
 
    if (FLAGS_compression) {
397
 
      tune_options |= kyotocabinet::TreeDB::TCOMPRESS;
398
 
      db_->tune_compressor(&comp_);
399
 
    }
400
 
    db_->tune_options(tune_options);
401
 
    db_->tune_page_cache(FLAGS_cache_size);
402
 
    db_->tune_page(FLAGS_page_size);
403
 
    db_->tune_map(256LL<<20);
404
 
    if (sync) {
405
 
      open_options |= kyotocabinet::PolyDB::OAUTOSYNC;
406
 
    }
407
 
    if (!db_->open(file_name, open_options)) {
408
 
      fprintf(stderr, "open error: %s\n", db_->error().name());
409
 
    }
410
 
  }
411
 
 
412
 
  void Write(bool sync, Order order, DBState state,
413
 
             int num_entries, int value_size, int entries_per_batch) {
414
 
    // Create new database if state == FRESH
415
 
    if (state == FRESH) {
416
 
      if (FLAGS_use_existing_db) {
417
 
        message_ = "skipping (--use_existing_db is true)";
418
 
        return;
419
 
      }
420
 
      delete db_;
421
 
      db_ = NULL;
422
 
      Open(sync);
423
 
      Start();  // Do not count time taken to destroy/open
424
 
    }
425
 
 
426
 
    if (num_entries != num_) {
427
 
      char msg[100];
428
 
      snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
429
 
      message_ = msg;
430
 
    }
431
 
 
432
 
    // Write to database
433
 
    for (int i = 0; i < num_entries; i++)
434
 
    {
435
 
      const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % num_entries);
436
 
      char key[100];
437
 
      snprintf(key, sizeof(key), "%016d", k);
438
 
      bytes_ += value_size + strlen(key);
439
 
      std::string cpp_key = key;
440
 
      if (!db_->set(cpp_key, gen_.Generate(value_size).ToString())) {
441
 
        fprintf(stderr, "set error: %s\n", db_->error().name());
442
 
      }
443
 
      FinishedSingleOp();
444
 
    }
445
 
  }
446
 
 
447
 
  void ReadSequential() {
448
 
    kyotocabinet::DB::Cursor* cur = db_->cursor();
449
 
    cur->jump();
450
 
    std::string ckey, cvalue;
451
 
    while (cur->get(&ckey, &cvalue, true)) {
452
 
      bytes_ += ckey.size() + cvalue.size();
453
 
      FinishedSingleOp();
454
 
    }
455
 
    delete cur;
456
 
  }
457
 
 
458
 
  void ReadRandom() {
459
 
    std::string value;
460
 
    for (int i = 0; i < reads_; i++) {
461
 
      char key[100];
462
 
      const int k = rand_.Next() % reads_;
463
 
      snprintf(key, sizeof(key), "%016d", k);
464
 
      db_->get(key, &value);
465
 
      FinishedSingleOp();
466
 
    }
467
 
  }
468
 
};
469
 
 
470
 
}  // namespace leveldb
471
 
 
472
 
int main(int argc, char** argv) {
473
 
  for (int i = 1; i < argc; i++) {
474
 
    double d;
475
 
    int n;
476
 
    char junk;
477
 
    if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
478
 
      FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
479
 
    } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
480
 
      FLAGS_compression_ratio = d;
481
 
    } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
482
 
               (n == 0 || n == 1)) {
483
 
      FLAGS_histogram = n;
484
 
    } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
485
 
      FLAGS_num = n;
486
 
    } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
487
 
      FLAGS_reads = n;
488
 
    } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
489
 
      FLAGS_value_size = n;
490
 
    } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) {
491
 
      FLAGS_cache_size = n;
492
 
    } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) {
493
 
      FLAGS_page_size = n;
494
 
    } else if (sscanf(argv[i], "--compression=%d%c", &n, &junk) == 1 &&
495
 
               (n == 0 || n == 1)) {
496
 
      FLAGS_compression = (n == 1) ? true : false;
497
 
    } else {
498
 
      fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
499
 
      exit(1);
500
 
    }
501
 
  }
502
 
 
503
 
  leveldb::Benchmark benchmark;
504
 
  benchmark.Run();
505
 
  return 0;
506
 
}