leveldb-ruby 0.7 → 0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. data/README +1 -1
  2. data/leveldb/Makefile +70 -29
  3. data/leveldb/build_detect_platform +74 -0
  4. data/leveldb/db/builder.cc +2 -4
  5. data/leveldb/db/builder.h +4 -6
  6. data/leveldb/db/c.cc +471 -0
  7. data/leveldb/db/corruption_test.cc +21 -16
  8. data/leveldb/db/db_bench.cc +400 -200
  9. data/leveldb/db/db_impl.cc +276 -131
  10. data/leveldb/db/db_impl.h +22 -10
  11. data/leveldb/db/db_iter.cc +2 -1
  12. data/leveldb/db/db_test.cc +391 -43
  13. data/leveldb/db/dbformat.cc +31 -0
  14. data/leveldb/db/dbformat.h +51 -1
  15. data/leveldb/db/filename.h +1 -1
  16. data/leveldb/db/log_format.h +1 -1
  17. data/leveldb/db/log_reader.cc +16 -11
  18. data/leveldb/db/memtable.cc +37 -0
  19. data/leveldb/db/memtable.h +6 -0
  20. data/leveldb/db/repair.cc +17 -14
  21. data/leveldb/db/skiplist_test.cc +2 -2
  22. data/leveldb/db/version_edit.cc +7 -9
  23. data/leveldb/db/version_edit.h +2 -1
  24. data/leveldb/db/version_set.cc +416 -104
  25. data/leveldb/db/version_set.h +78 -14
  26. data/leveldb/db/version_set_test.cc +179 -0
  27. data/leveldb/db/write_batch_internal.h +2 -0
  28. data/leveldb/include/leveldb/c.h +246 -0
  29. data/leveldb/include/leveldb/db.h +14 -2
  30. data/leveldb/include/leveldb/env.h +31 -10
  31. data/leveldb/include/leveldb/options.h +7 -18
  32. data/leveldb/include/leveldb/slice.h +2 -2
  33. data/leveldb/include/leveldb/status.h +1 -1
  34. data/leveldb/port/atomic_pointer.h +144 -0
  35. data/leveldb/port/port.h +0 -2
  36. data/leveldb/port/port_android.h +7 -1
  37. data/leveldb/port/port_example.h +11 -1
  38. data/leveldb/port/port_posix.h +56 -38
  39. data/leveldb/table/format.cc +12 -8
  40. data/leveldb/table/table_test.cc +16 -7
  41. data/leveldb/util/cache.cc +173 -100
  42. data/leveldb/util/cache_test.cc +28 -11
  43. data/leveldb/util/coding.h +4 -4
  44. data/leveldb/util/comparator.cc +1 -0
  45. data/leveldb/util/env.cc +10 -5
  46. data/leveldb/util/env_posix.cc +48 -87
  47. data/leveldb/util/histogram.cc +11 -0
  48. data/leveldb/util/histogram.h +1 -0
  49. data/leveldb/util/posix_logger.h +98 -0
  50. data/leveldb/util/testharness.cc +12 -0
  51. data/leveldb/util/testharness.h +10 -1
  52. data/lib/leveldb.rb +11 -3
  53. metadata +41 -22
@@ -27,13 +27,12 @@ static const int kValueSize = 1000;
27
27
  class CorruptionTest {
28
28
  public:
29
29
  test::ErrorEnv env_;
30
- Random rnd_;
31
30
  std::string dbname_;
32
31
  Cache* tiny_cache_;
33
32
  Options options_;
34
33
  DB* db_;
35
34
 
36
- CorruptionTest() : rnd_(test::RandomSeed()) {
35
+ CorruptionTest() {
37
36
  tiny_cache_ = NewLRUCache(100);
38
37
  options_.env = &env_;
39
38
  dbname_ = test::TmpDir() + "/db_test";
@@ -122,15 +121,17 @@ class CorruptionTest {
122
121
  ASSERT_OK(env_.GetChildren(dbname_, &filenames));
123
122
  uint64_t number;
124
123
  FileType type;
125
- std::vector<std::string> candidates;
124
+ std::string fname;
125
+ int picked_number = -1;
126
126
  for (int i = 0; i < filenames.size(); i++) {
127
127
  if (ParseFileName(filenames[i], &number, &type) &&
128
- type == filetype) {
129
- candidates.push_back(dbname_ + "/" + filenames[i]);
128
+ type == filetype &&
129
+ int(number) > picked_number) { // Pick latest file
130
+ fname = dbname_ + "/" + filenames[i];
131
+ picked_number = number;
130
132
  }
131
133
  }
132
- ASSERT_TRUE(!candidates.empty()) << filetype;
133
- std::string fname = candidates[rnd_.Uniform(candidates.size())];
134
+ ASSERT_TRUE(!fname.empty()) << filetype;
134
135
 
135
136
  struct stat sbuf;
136
137
  if (stat(fname.c_str(), &sbuf) != 0) {
@@ -228,8 +229,8 @@ TEST(CorruptionTest, TableFile) {
228
229
  Build(100);
229
230
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
230
231
  dbi->TEST_CompactMemTable();
231
- dbi->TEST_CompactRange(0, "", "~");
232
- dbi->TEST_CompactRange(1, "", "~");
232
+ dbi->TEST_CompactRange(0, NULL, NULL);
233
+ dbi->TEST_CompactRange(1, NULL, NULL);
233
234
 
234
235
  Corrupt(kTableFile, 100, 1);
235
236
  Check(99, 99);
@@ -239,8 +240,6 @@ TEST(CorruptionTest, TableFileIndexData) {
239
240
  Build(10000); // Enough to build multiple Tables
240
241
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
241
242
  dbi->TEST_CompactMemTable();
242
- dbi->TEST_CompactRange(0, "", "~");
243
- dbi->TEST_CompactRange(1, "", "~");
244
243
 
245
244
  Corrupt(kTableFile, -2000, 500);
246
245
  Reopen();
@@ -279,7 +278,7 @@ TEST(CorruptionTest, CorruptedDescriptor) {
279
278
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
280
279
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
281
280
  dbi->TEST_CompactMemTable();
282
- dbi->TEST_CompactRange(0, "", "~");
281
+ dbi->TEST_CompactRange(0, NULL, NULL);
283
282
 
284
283
  Corrupt(kDescriptorFile, 0, 1000);
285
284
  Status s = TryReopen();
@@ -296,7 +295,8 @@ TEST(CorruptionTest, CompactionInputError) {
296
295
  Build(10);
297
296
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
298
297
  dbi->TEST_CompactMemTable();
299
- ASSERT_EQ(1, Property("leveldb.num-files-at-level0"));
298
+ const int last = config::kMaxMemCompactLevel;
299
+ ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last)));
300
300
 
301
301
  Corrupt(kTableFile, 100, 1);
302
302
  Check(9, 9);
@@ -304,8 +304,6 @@ TEST(CorruptionTest, CompactionInputError) {
304
304
  // Force compactions by writing lots of values
305
305
  Build(10000);
306
306
  Check(10000, 10000);
307
- dbi->TEST_CompactRange(0, "", "~");
308
- ASSERT_EQ(0, Property("leveldb.num-files-at-level0"));
309
307
  }
310
308
 
311
309
  TEST(CorruptionTest, CompactionInputErrorParanoid) {
@@ -313,9 +311,16 @@ TEST(CorruptionTest, CompactionInputErrorParanoid) {
313
311
  options.paranoid_checks = true;
314
312
  options.write_buffer_size = 1048576;
315
313
  Reopen(&options);
314
+ DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
315
+
316
+ // Fill levels >= 1 so memtable compaction outputs to level 1
317
+ for (int level = 1; level < config::kNumLevels; level++) {
318
+ dbi->Put(WriteOptions(), "", "begin");
319
+ dbi->Put(WriteOptions(), "~", "end");
320
+ dbi->TEST_CompactMemTable();
321
+ }
316
322
 
317
323
  Build(10);
318
- DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
319
324
  dbi->TEST_CompactMemTable();
320
325
  ASSERT_EQ(1, Property("leveldb.num-files-at-level0"));
321
326
 
@@ -14,6 +14,7 @@
14
14
  #include "port/port.h"
15
15
  #include "util/crc32c.h"
16
16
  #include "util/histogram.h"
17
+ #include "util/mutexlock.h"
17
18
  #include "util/random.h"
18
19
  #include "util/testutil.h"
19
20
 
@@ -60,6 +61,9 @@ static int FLAGS_num = 1000000;
60
61
  // Number of read operations to do. If negative, do FLAGS_num reads.
61
62
  static int FLAGS_reads = -1;
62
63
 
64
+ // Number of concurrent threads to run.
65
+ static int FLAGS_threads = 1;
66
+
63
67
  // Size of each value
64
68
  static int FLAGS_value_size = 100;
65
69
 
@@ -86,10 +90,14 @@ static int FLAGS_open_files = 0;
86
90
  // benchmark will fail.
87
91
  static bool FLAGS_use_existing_db = false;
88
92
 
93
+ // Use the db with the following name.
94
+ static const char* FLAGS_db = "/tmp/dbbench";
95
+
89
96
  namespace leveldb {
90
97
 
91
- // Helper for quickly generating random data.
92
98
  namespace {
99
+
100
+ // Helper for quickly generating random data.
93
101
  class RandomGenerator {
94
102
  private:
95
103
  std::string data_;
@@ -133,6 +141,153 @@ static Slice TrimSpace(Slice s) {
133
141
  return Slice(s.data() + start, limit - start);
134
142
  }
135
143
 
144
+ static void AppendWithSpace(std::string* str, Slice msg) {
145
+ if (msg.empty()) return;
146
+ if (!str->empty()) {
147
+ str->push_back(' ');
148
+ }
149
+ str->append(msg.data(), msg.size());
150
+ }
151
+
152
+ class Stats {
153
+ private:
154
+ double start_;
155
+ double finish_;
156
+ double seconds_;
157
+ int done_;
158
+ int next_report_;
159
+ int64_t bytes_;
160
+ double last_op_finish_;
161
+ Histogram hist_;
162
+ std::string message_;
163
+
164
+ public:
165
+ Stats() { Start(); }
166
+
167
+ void Start() {
168
+ next_report_ = 100;
169
+ last_op_finish_ = start_;
170
+ hist_.Clear();
171
+ done_ = 0;
172
+ bytes_ = 0;
173
+ seconds_ = 0;
174
+ start_ = Env::Default()->NowMicros();
175
+ finish_ = start_;
176
+ message_.clear();
177
+ }
178
+
179
+ void Merge(const Stats& other) {
180
+ hist_.Merge(other.hist_);
181
+ done_ += other.done_;
182
+ bytes_ += other.bytes_;
183
+ seconds_ += other.seconds_;
184
+ if (other.start_ < start_) start_ = other.start_;
185
+ if (other.finish_ > finish_) finish_ = other.finish_;
186
+
187
+ // Just keep the messages from one thread
188
+ if (message_.empty()) message_ = other.message_;
189
+ }
190
+
191
+ void Stop() {
192
+ finish_ = Env::Default()->NowMicros();
193
+ seconds_ = (finish_ - start_) * 1e-6;
194
+ }
195
+
196
+ void AddMessage(Slice msg) {
197
+ AppendWithSpace(&message_, msg);
198
+ }
199
+
200
+ void FinishedSingleOp() {
201
+ if (FLAGS_histogram) {
202
+ double now = Env::Default()->NowMicros();
203
+ double micros = now - last_op_finish_;
204
+ hist_.Add(micros);
205
+ if (micros > 20000) {
206
+ fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
207
+ fflush(stderr);
208
+ }
209
+ last_op_finish_ = now;
210
+ }
211
+
212
+ done_++;
213
+ if (done_ >= next_report_) {
214
+ if (next_report_ < 1000) next_report_ += 100;
215
+ else if (next_report_ < 5000) next_report_ += 500;
216
+ else if (next_report_ < 10000) next_report_ += 1000;
217
+ else if (next_report_ < 50000) next_report_ += 5000;
218
+ else if (next_report_ < 100000) next_report_ += 10000;
219
+ else if (next_report_ < 500000) next_report_ += 50000;
220
+ else next_report_ += 100000;
221
+ fprintf(stderr, "... finished %d ops%30s\r", done_, "");
222
+ fflush(stderr);
223
+ }
224
+ }
225
+
226
+ void AddBytes(int64_t n) {
227
+ bytes_ += n;
228
+ }
229
+
230
+ void Report(const Slice& name) {
231
+ // Pretend at least one op was done in case we are running a benchmark
232
+ // that does not call FinishedSingleOp().
233
+ if (done_ < 1) done_ = 1;
234
+
235
+ std::string extra;
236
+ if (bytes_ > 0) {
237
+ // Rate is computed on actual elapsed time, not the sum of per-thread
238
+ // elapsed times.
239
+ double elapsed = (finish_ - start_) * 1e-6;
240
+ char rate[100];
241
+ snprintf(rate, sizeof(rate), "%6.1f MB/s",
242
+ (bytes_ / 1048576.0) / elapsed);
243
+ extra = rate;
244
+ }
245
+ AppendWithSpace(&extra, message_);
246
+
247
+ fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
248
+ name.ToString().c_str(),
249
+ seconds_ * 1e6 / done_,
250
+ (extra.empty() ? "" : " "),
251
+ extra.c_str());
252
+ if (FLAGS_histogram) {
253
+ fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
254
+ }
255
+ fflush(stdout);
256
+ }
257
+ };
258
+
259
+ // State shared by all concurrent executions of the same benchmark.
260
+ struct SharedState {
261
+ port::Mutex mu;
262
+ port::CondVar cv;
263
+ int total;
264
+
265
+ // Each thread goes through the following states:
266
+ // (1) initializing
267
+ // (2) waiting for others to be initialized
268
+ // (3) running
269
+ // (4) done
270
+
271
+ int num_initialized;
272
+ int num_done;
273
+ bool start;
274
+
275
+ SharedState() : cv(&mu) { }
276
+ };
277
+
278
+ // Per-thread state for concurrent executions of the same benchmark.
279
+ struct ThreadState {
280
+ int tid; // 0..n-1 when running in n threads
281
+ Random rand; // Has different seeds for different threads
282
+ Stats stats;
283
+ SharedState* shared;
284
+
285
+ ThreadState(int index)
286
+ : tid(index),
287
+ rand(1000 + index) {
288
+ }
289
+ };
290
+
136
291
  }
137
292
 
138
293
  class Benchmark {
@@ -140,20 +295,11 @@ class Benchmark {
140
295
  Cache* cache_;
141
296
  DB* db_;
142
297
  int num_;
298
+ int value_size_;
299
+ int entries_per_batch_;
300
+ WriteOptions write_options_;
143
301
  int reads_;
144
302
  int heap_counter_;
145
- double start_;
146
- double last_op_finish_;
147
- int64_t bytes_;
148
- std::string message_;
149
- std::string post_message_;
150
- Histogram hist_;
151
- RandomGenerator gen_;
152
- Random rand_;
153
-
154
- // State kept for progress messages
155
- int done_;
156
- int next_report_; // When to report next
157
303
 
158
304
  void PrintHeader() {
159
305
  const int kKeySize = 16;
@@ -229,103 +375,24 @@ class Benchmark {
229
375
  #endif
230
376
  }
231
377
 
232
- void Start() {
233
- start_ = Env::Default()->NowMicros() * 1e-6;
234
- bytes_ = 0;
235
- message_.clear();
236
- last_op_finish_ = start_;
237
- hist_.Clear();
238
- done_ = 0;
239
- next_report_ = 100;
240
- }
241
-
242
- void FinishedSingleOp() {
243
- if (FLAGS_histogram) {
244
- double now = Env::Default()->NowMicros() * 1e-6;
245
- double micros = (now - last_op_finish_) * 1e6;
246
- hist_.Add(micros);
247
- if (micros > 20000) {
248
- fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
249
- fflush(stderr);
250
- }
251
- last_op_finish_ = now;
252
- }
253
-
254
- done_++;
255
- if (done_ >= next_report_) {
256
- if (next_report_ < 1000) next_report_ += 100;
257
- else if (next_report_ < 5000) next_report_ += 500;
258
- else if (next_report_ < 10000) next_report_ += 1000;
259
- else if (next_report_ < 50000) next_report_ += 5000;
260
- else if (next_report_ < 100000) next_report_ += 10000;
261
- else if (next_report_ < 500000) next_report_ += 50000;
262
- else next_report_ += 100000;
263
- fprintf(stderr, "... finished %d ops%30s\r", done_, "");
264
- fflush(stderr);
265
- }
266
- }
267
-
268
- void Stop(const Slice& name) {
269
- double finish = Env::Default()->NowMicros() * 1e-6;
270
-
271
- // Pretend at least one op was done in case we are running a benchmark
272
- // that does nto call FinishedSingleOp().
273
- if (done_ < 1) done_ = 1;
274
-
275
- if (bytes_ > 0) {
276
- char rate[100];
277
- snprintf(rate, sizeof(rate), "%6.1f MB/s",
278
- (bytes_ / 1048576.0) / (finish - start_));
279
- if (!message_.empty()) {
280
- message_ = std::string(rate) + " " + message_;
281
- } else {
282
- message_ = rate;
283
- }
284
- }
285
-
286
- fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
287
- name.ToString().c_str(),
288
- (finish - start_) * 1e6 / done_,
289
- (message_.empty() ? "" : " "),
290
- message_.c_str());
291
- if (FLAGS_histogram) {
292
- fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
293
- }
294
- fflush(stdout);
295
-
296
- if (!post_message_.empty()) {
297
- fprintf(stdout, "\n%s\n", post_message_.c_str());
298
- post_message_.clear();
299
- }
300
- }
301
-
302
378
  public:
303
- enum Order {
304
- SEQUENTIAL,
305
- RANDOM
306
- };
307
- enum DBState {
308
- FRESH,
309
- EXISTING
310
- };
311
-
312
379
  Benchmark()
313
380
  : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL),
314
381
  db_(NULL),
315
382
  num_(FLAGS_num),
383
+ value_size_(FLAGS_value_size),
384
+ entries_per_batch_(1),
316
385
  reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
317
- heap_counter_(0),
318
- bytes_(0),
319
- rand_(301) {
386
+ heap_counter_(0) {
320
387
  std::vector<std::string> files;
321
- Env::Default()->GetChildren("/tmp/dbbench", &files);
388
+ Env::Default()->GetChildren(FLAGS_db, &files);
322
389
  for (int i = 0; i < files.size(); i++) {
323
390
  if (Slice(files[i]).starts_with("heap-")) {
324
- Env::Default()->DeleteFile("/tmp/dbbench/" + files[i]);
391
+ Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]);
325
392
  }
326
393
  }
327
394
  if (!FLAGS_use_existing_db) {
328
- DestroyDB("/tmp/dbbench", Options());
395
+ DestroyDB(FLAGS_db, Options());
329
396
  }
330
397
  }
331
398
 
@@ -350,98 +417,208 @@ class Benchmark {
350
417
  benchmarks = sep + 1;
351
418
  }
352
419
 
353
- Start();
420
+ // Reset parameters that may be overriddden bwlow
421
+ num_ = FLAGS_num;
422
+ reads_ = (FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads);
423
+ value_size_ = FLAGS_value_size;
424
+ entries_per_batch_ = 1;
425
+ write_options_ = WriteOptions();
426
+
427
+ void (Benchmark::*method)(ThreadState*) = NULL;
428
+ bool fresh_db = false;
429
+ int num_threads = FLAGS_threads;
354
430
 
355
- WriteOptions write_options;
356
- bool known = true;
357
431
  if (name == Slice("fillseq")) {
358
- Write(write_options, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1);
432
+ fresh_db = true;
433
+ method = &Benchmark::WriteSeq;
359
434
  } else if (name == Slice("fillbatch")) {
360
- Write(write_options, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1000);
435
+ fresh_db = true;
436
+ entries_per_batch_ = 1000;
437
+ method = &Benchmark::WriteSeq;
361
438
  } else if (name == Slice("fillrandom")) {
362
- Write(write_options, RANDOM, FRESH, num_, FLAGS_value_size, 1);
439
+ fresh_db = true;
440
+ method = &Benchmark::WriteRandom;
363
441
  } else if (name == Slice("overwrite")) {
364
- Write(write_options, RANDOM, EXISTING, num_, FLAGS_value_size, 1);
442
+ fresh_db = false;
443
+ method = &Benchmark::WriteRandom;
365
444
  } else if (name == Slice("fillsync")) {
366
- write_options.sync = true;
367
- Write(write_options, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1);
445
+ fresh_db = true;
446
+ num_ /= 1000;
447
+ write_options_.sync = true;
448
+ method = &Benchmark::WriteRandom;
368
449
  } else if (name == Slice("fill100K")) {
369
- Write(write_options, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1);
450
+ fresh_db = true;
451
+ num_ /= 1000;
452
+ value_size_ = 100 * 1000;
453
+ method = &Benchmark::WriteRandom;
370
454
  } else if (name == Slice("readseq")) {
371
- ReadSequential();
455
+ method = &Benchmark::ReadSequential;
372
456
  } else if (name == Slice("readreverse")) {
373
- ReadReverse();
457
+ method = &Benchmark::ReadReverse;
374
458
  } else if (name == Slice("readrandom")) {
375
- ReadRandom();
459
+ method = &Benchmark::ReadRandom;
376
460
  } else if (name == Slice("readhot")) {
377
- ReadHot();
461
+ method = &Benchmark::ReadHot;
378
462
  } else if (name == Slice("readrandomsmall")) {
379
- int n = reads_;
380
463
  reads_ /= 1000;
381
- ReadRandom();
382
- reads_ = n;
464
+ method = &Benchmark::ReadRandom;
465
+ } else if (name == Slice("readwhilewriting")) {
466
+ num_threads++; // Add extra thread for writing
467
+ method = &Benchmark::ReadWhileWriting;
383
468
  } else if (name == Slice("compact")) {
384
- Compact();
469
+ method = &Benchmark::Compact;
385
470
  } else if (name == Slice("crc32c")) {
386
- Crc32c(4096, "(4K per op)");
471
+ method = &Benchmark::Crc32c;
387
472
  } else if (name == Slice("acquireload")) {
388
- AcquireLoad();
473
+ method = &Benchmark::AcquireLoad;
389
474
  } else if (name == Slice("snappycomp")) {
390
- SnappyCompress();
475
+ method = &Benchmark::SnappyCompress;
391
476
  } else if (name == Slice("snappyuncomp")) {
392
- SnappyUncompress();
477
+ method = &Benchmark::SnappyUncompress;
393
478
  } else if (name == Slice("heapprofile")) {
394
479
  HeapProfile();
395
480
  } else if (name == Slice("stats")) {
396
481
  PrintStats();
397
482
  } else {
398
- known = false;
399
483
  if (name != Slice()) { // No error message for empty name
400
484
  fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
401
485
  }
402
486
  }
403
- if (known) {
404
- Stop(name);
487
+
488
+ if (fresh_db) {
489
+ if (FLAGS_use_existing_db) {
490
+ fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n",
491
+ name.ToString().c_str());
492
+ method = NULL;
493
+ } else {
494
+ delete db_;
495
+ db_ = NULL;
496
+ DestroyDB(FLAGS_db, Options());
497
+ Open();
498
+ }
499
+ }
500
+
501
+ if (method != NULL) {
502
+ RunBenchmark(num_threads, name, method);
405
503
  }
406
504
  }
407
505
  }
408
506
 
409
507
  private:
410
- void Crc32c(int size, const char* label) {
508
+ struct ThreadArg {
509
+ Benchmark* bm;
510
+ SharedState* shared;
511
+ ThreadState* thread;
512
+ void (Benchmark::*method)(ThreadState*);
513
+ };
514
+
515
+ static void ThreadBody(void* v) {
516
+ ThreadArg* arg = reinterpret_cast<ThreadArg*>(v);
517
+ SharedState* shared = arg->shared;
518
+ ThreadState* thread = arg->thread;
519
+ {
520
+ MutexLock l(&shared->mu);
521
+ shared->num_initialized++;
522
+ if (shared->num_initialized >= shared->total) {
523
+ shared->cv.SignalAll();
524
+ }
525
+ while (!shared->start) {
526
+ shared->cv.Wait();
527
+ }
528
+ }
529
+
530
+ thread->stats.Start();
531
+ (arg->bm->*(arg->method))(thread);
532
+ thread->stats.Stop();
533
+
534
+ {
535
+ MutexLock l(&shared->mu);
536
+ shared->num_done++;
537
+ if (shared->num_done >= shared->total) {
538
+ shared->cv.SignalAll();
539
+ }
540
+ }
541
+ }
542
+
543
+ void RunBenchmark(int n, Slice name,
544
+ void (Benchmark::*method)(ThreadState*)) {
545
+ SharedState shared;
546
+ shared.total = n;
547
+ shared.num_initialized = 0;
548
+ shared.num_done = 0;
549
+ shared.start = false;
550
+
551
+ ThreadArg* arg = new ThreadArg[n];
552
+ for (int i = 0; i < n; i++) {
553
+ arg[i].bm = this;
554
+ arg[i].method = method;
555
+ arg[i].shared = &shared;
556
+ arg[i].thread = new ThreadState(i);
557
+ arg[i].thread->shared = &shared;
558
+ Env::Default()->StartThread(ThreadBody, &arg[i]);
559
+ }
560
+
561
+ shared.mu.Lock();
562
+ while (shared.num_initialized < n) {
563
+ shared.cv.Wait();
564
+ }
565
+
566
+ shared.start = true;
567
+ shared.cv.SignalAll();
568
+ while (shared.num_done < n) {
569
+ shared.cv.Wait();
570
+ }
571
+ shared.mu.Unlock();
572
+
573
+ for (int i = 1; i < n; i++) {
574
+ arg[0].thread->stats.Merge(arg[i].thread->stats);
575
+ }
576
+ arg[0].thread->stats.Report(name);
577
+
578
+ for (int i = 0; i < n; i++) {
579
+ delete arg[i].thread;
580
+ }
581
+ delete[] arg;
582
+ }
583
+
584
+ void Crc32c(ThreadState* thread) {
411
585
  // Checksum about 500MB of data total
586
+ const int size = 4096;
587
+ const char* label = "(4K per op)";
412
588
  std::string data(size, 'x');
413
589
  int64_t bytes = 0;
414
590
  uint32_t crc = 0;
415
591
  while (bytes < 500 * 1048576) {
416
592
  crc = crc32c::Value(data.data(), size);
417
- FinishedSingleOp();
593
+ thread->stats.FinishedSingleOp();
418
594
  bytes += size;
419
595
  }
420
596
  // Print so result is not dead
421
597
  fprintf(stderr, "... crc=0x%x\r", static_cast<unsigned int>(crc));
422
598
 
423
- bytes_ = bytes;
424
- message_ = label;
599
+ thread->stats.AddBytes(bytes);
600
+ thread->stats.AddMessage(label);
425
601
  }
426
602
 
427
- void AcquireLoad() {
603
+ void AcquireLoad(ThreadState* thread) {
428
604
  int dummy;
429
605
  port::AtomicPointer ap(&dummy);
430
606
  int count = 0;
431
607
  void *ptr = NULL;
432
- message_ = "(each op is 1000 loads)";
608
+ thread->stats.AddMessage("(each op is 1000 loads)");
433
609
  while (count < 100000) {
434
610
  for (int i = 0; i < 1000; i++) {
435
611
  ptr = ap.Acquire_Load();
436
612
  }
437
613
  count++;
438
- FinishedSingleOp();
614
+ thread->stats.FinishedSingleOp();
439
615
  }
440
616
  if (ptr == NULL) exit(1); // Disable unused variable warning.
441
617
  }
442
618
 
443
- void SnappyCompress() {
444
- Slice input = gen_.Generate(Options().block_size);
619
+ void SnappyCompress(ThreadState* thread) {
620
+ RandomGenerator gen;
621
+ Slice input = gen.Generate(Options().block_size);
445
622
  int64_t bytes = 0;
446
623
  int64_t produced = 0;
447
624
  bool ok = true;
@@ -450,37 +627,39 @@ class Benchmark {
450
627
  ok = port::Snappy_Compress(input.data(), input.size(), &compressed);
451
628
  produced += compressed.size();
452
629
  bytes += input.size();
453
- FinishedSingleOp();
630
+ thread->stats.FinishedSingleOp();
454
631
  }
455
632
 
456
633
  if (!ok) {
457
- message_ = "(snappy failure)";
634
+ thread->stats.AddMessage("(snappy failure)");
458
635
  } else {
459
636
  char buf[100];
460
637
  snprintf(buf, sizeof(buf), "(output: %.1f%%)",
461
638
  (produced * 100.0) / bytes);
462
- message_ = buf;
463
- bytes_ = bytes;
639
+ thread->stats.AddMessage(buf);
640
+ thread->stats.AddBytes(bytes);
464
641
  }
465
642
  }
466
643
 
467
- void SnappyUncompress() {
468
- Slice input = gen_.Generate(Options().block_size);
644
+ void SnappyUncompress(ThreadState* thread) {
645
+ RandomGenerator gen;
646
+ Slice input = gen.Generate(Options().block_size);
469
647
  std::string compressed;
470
648
  bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed);
471
649
  int64_t bytes = 0;
472
- std::string uncompressed;
650
+ char* uncompressed = new char[input.size()];
473
651
  while (ok && bytes < 1024 * 1048576) { // Compress 1G
474
652
  ok = port::Snappy_Uncompress(compressed.data(), compressed.size(),
475
- &uncompressed);
476
- bytes += uncompressed.size();
477
- FinishedSingleOp();
653
+ uncompressed);
654
+ bytes += input.size();
655
+ thread->stats.FinishedSingleOp();
478
656
  }
657
+ delete[] uncompressed;
479
658
 
480
659
  if (!ok) {
481
- message_ = "(snappy failure)";
660
+ thread->stats.AddMessage("(snappy failure)");
482
661
  } else {
483
- bytes_ = bytes;
662
+ thread->stats.AddBytes(bytes);
484
663
  }
485
664
  }
486
665
 
@@ -490,125 +669,142 @@ class Benchmark {
490
669
  options.create_if_missing = !FLAGS_use_existing_db;
491
670
  options.block_cache = cache_;
492
671
  options.write_buffer_size = FLAGS_write_buffer_size;
493
- Status s = DB::Open(options, "/tmp/dbbench", &db_);
672
+ Status s = DB::Open(options, FLAGS_db, &db_);
494
673
  if (!s.ok()) {
495
674
  fprintf(stderr, "open error: %s\n", s.ToString().c_str());
496
675
  exit(1);
497
676
  }
498
677
  }
499
678
 
500
- void Write(const WriteOptions& options, Order order, DBState state,
501
- int num_entries, int value_size, int entries_per_batch) {
502
- if (state == FRESH) {
503
- if (FLAGS_use_existing_db) {
504
- message_ = "skipping (--use_existing_db is true)";
505
- return;
506
- }
507
- delete db_;
508
- db_ = NULL;
509
- DestroyDB("/tmp/dbbench", Options());
510
- Open();
511
- Start(); // Do not count time taken to destroy/open
512
- }
679
+ void WriteSeq(ThreadState* thread) {
680
+ DoWrite(thread, true);
681
+ }
682
+
683
+ void WriteRandom(ThreadState* thread) {
684
+ DoWrite(thread, false);
685
+ }
513
686
 
514
- if (num_entries != num_) {
687
+ void DoWrite(ThreadState* thread, bool seq) {
688
+ if (num_ != FLAGS_num) {
515
689
  char msg[100];
516
- snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
517
- message_ = msg;
690
+ snprintf(msg, sizeof(msg), "(%d ops)", num_);
691
+ thread->stats.AddMessage(msg);
518
692
  }
519
693
 
694
+ RandomGenerator gen;
520
695
  WriteBatch batch;
521
696
  Status s;
522
- std::string val;
523
- for (int i = 0; i < num_entries; i += entries_per_batch) {
697
+ int64_t bytes = 0;
698
+ for (int i = 0; i < num_; i += entries_per_batch_) {
524
699
  batch.Clear();
525
- for (int j = 0; j < entries_per_batch; j++) {
526
- const int k = (order == SEQUENTIAL) ? i+j : (rand_.Next() % FLAGS_num);
700
+ for (int j = 0; j < entries_per_batch_; j++) {
701
+ const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num);
527
702
  char key[100];
528
703
  snprintf(key, sizeof(key), "%016d", k);
529
- batch.Put(key, gen_.Generate(value_size));
530
- bytes_ += value_size + strlen(key);
531
- FinishedSingleOp();
704
+ batch.Put(key, gen.Generate(value_size_));
705
+ bytes += value_size_ + strlen(key);
706
+ thread->stats.FinishedSingleOp();
532
707
  }
533
- s = db_->Write(options, &batch);
708
+ s = db_->Write(write_options_, &batch);
534
709
  if (!s.ok()) {
535
710
  fprintf(stderr, "put error: %s\n", s.ToString().c_str());
536
711
  exit(1);
537
712
  }
538
713
  }
714
+ thread->stats.AddBytes(bytes);
539
715
  }
540
716
 
541
- void ReadSequential() {
717
+ void ReadSequential(ThreadState* thread) {
542
718
  Iterator* iter = db_->NewIterator(ReadOptions());
543
719
  int i = 0;
720
+ int64_t bytes = 0;
544
721
  for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) {
545
- bytes_ += iter->key().size() + iter->value().size();
546
- FinishedSingleOp();
722
+ bytes += iter->key().size() + iter->value().size();
723
+ thread->stats.FinishedSingleOp();
547
724
  ++i;
548
725
  }
549
726
  delete iter;
727
+ thread->stats.AddBytes(bytes);
550
728
  }
551
729
 
552
- void ReadReverse() {
730
+ void ReadReverse(ThreadState* thread) {
553
731
  Iterator* iter = db_->NewIterator(ReadOptions());
554
732
  int i = 0;
733
+ int64_t bytes = 0;
555
734
  for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) {
556
- bytes_ += iter->key().size() + iter->value().size();
557
- FinishedSingleOp();
735
+ bytes += iter->key().size() + iter->value().size();
736
+ thread->stats.FinishedSingleOp();
558
737
  ++i;
559
738
  }
560
739
  delete iter;
740
+ thread->stats.AddBytes(bytes);
561
741
  }
562
742
 
563
- void ReadRandom() {
743
+ void ReadRandom(ThreadState* thread) {
564
744
  ReadOptions options;
565
745
  std::string value;
566
746
  for (int i = 0; i < reads_; i++) {
567
747
  char key[100];
568
- const int k = rand_.Next() % FLAGS_num;
748
+ const int k = thread->rand.Next() % FLAGS_num;
569
749
  snprintf(key, sizeof(key), "%016d", k);
570
750
  db_->Get(options, key, &value);
571
- FinishedSingleOp();
751
+ thread->stats.FinishedSingleOp();
572
752
  }
573
753
  }
574
754
 
575
- void ReadHot() {
755
+ void ReadHot(ThreadState* thread) {
576
756
  ReadOptions options;
577
757
  std::string value;
578
758
  const int range = (FLAGS_num + 99) / 100;
579
759
  for (int i = 0; i < reads_; i++) {
580
760
  char key[100];
581
- const int k = rand_.Next() % range;
761
+ const int k = thread->rand.Next() % range;
582
762
  snprintf(key, sizeof(key), "%016d", k);
583
763
  db_->Get(options, key, &value);
584
- FinishedSingleOp();
764
+ thread->stats.FinishedSingleOp();
585
765
  }
586
766
  }
587
767
 
588
- void Compact() {
589
- DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
590
- dbi->TEST_CompactMemTable();
591
- int max_level_with_files = 1;
592
- for (int level = 1; level < config::kNumLevels; level++) {
593
- std::string property;
594
- char name[100];
595
- snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level);
596
- if (db_->GetProperty(name, &property) && atoi(property.c_str()) > 0) {
597
- max_level_with_files = level;
768
+ void ReadWhileWriting(ThreadState* thread) {
769
+ if (thread->tid > 0) {
770
+ ReadRandom(thread);
771
+ } else {
772
+ // Special thread that keeps writing until other threads are done.
773
+ RandomGenerator gen;
774
+ while (true) {
775
+ {
776
+ MutexLock l(&thread->shared->mu);
777
+ if (thread->shared->num_done + 1 >= thread->shared->num_initialized) {
778
+ // Other threads have finished
779
+ break;
780
+ }
781
+ }
782
+
783
+ const int k = thread->rand.Next() % FLAGS_num;
784
+ char key[100];
785
+ snprintf(key, sizeof(key), "%016d", k);
786
+ Status s = db_->Put(write_options_, key, gen.Generate(value_size_));
787
+ if (!s.ok()) {
788
+ fprintf(stderr, "put error: %s\n", s.ToString().c_str());
789
+ exit(1);
790
+ }
598
791
  }
599
- }
600
- for (int level = 0; level < max_level_with_files; level++) {
601
- dbi->TEST_CompactRange(level, "", "~");
792
+
793
+ // Do not count any of the preceding work/delay in stats.
794
+ thread->stats.Start();
602
795
  }
603
796
  }
604
797
 
798
+ void Compact(ThreadState* thread) {
799
+ db_->CompactRange(NULL, NULL);
800
+ }
801
+
605
802
  void PrintStats() {
606
803
  std::string stats;
607
804
  if (!db_->GetProperty("leveldb.stats", &stats)) {
608
- message_ = "(failed)";
609
- } else {
610
- post_message_ = stats;
805
+ stats = "(failed)";
611
806
  }
807
+ fprintf(stdout, "\n%s\n", stats.c_str());
612
808
  }
613
809
 
614
810
  static void WriteToFile(void* arg, const char* buf, int n) {
@@ -617,17 +813,17 @@ class Benchmark {
617
813
 
618
814
  void HeapProfile() {
619
815
  char fname[100];
620
- snprintf(fname, sizeof(fname), "/tmp/dbbench/heap-%04d", ++heap_counter_);
816
+ snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_);
621
817
  WritableFile* file;
622
818
  Status s = Env::Default()->NewWritableFile(fname, &file);
623
819
  if (!s.ok()) {
624
- message_ = s.ToString();
820
+ fprintf(stderr, "%s\n", s.ToString().c_str());
625
821
  return;
626
822
  }
627
823
  bool ok = port::GetHeapProfile(WriteToFile, file);
628
824
  delete file;
629
825
  if (!ok) {
630
- message_ = "not supported";
826
+ fprintf(stderr, "heap profiling not supported\n");
631
827
  Env::Default()->DeleteFile(fname);
632
828
  }
633
829
  }
@@ -657,6 +853,8 @@ int main(int argc, char** argv) {
657
853
  FLAGS_num = n;
658
854
  } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
659
855
  FLAGS_reads = n;
856
+ } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) {
857
+ FLAGS_threads = n;
660
858
  } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
661
859
  FLAGS_value_size = n;
662
860
  } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) {
@@ -665,6 +863,8 @@ int main(int argc, char** argv) {
665
863
  FLAGS_cache_size = n;
666
864
  } else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) {
667
865
  FLAGS_open_files = n;
866
+ } else if (strncmp(argv[i], "--db=", 5) == 0) {
867
+ FLAGS_db = argv[i] + 5;
668
868
  } else {
669
869
  fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
670
870
  exit(1);