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.
- data/README +1 -1
- data/leveldb/Makefile +70 -29
- data/leveldb/build_detect_platform +74 -0
- data/leveldb/db/builder.cc +2 -4
- data/leveldb/db/builder.h +4 -6
- data/leveldb/db/c.cc +471 -0
- data/leveldb/db/corruption_test.cc +21 -16
- data/leveldb/db/db_bench.cc +400 -200
- data/leveldb/db/db_impl.cc +276 -131
- data/leveldb/db/db_impl.h +22 -10
- data/leveldb/db/db_iter.cc +2 -1
- data/leveldb/db/db_test.cc +391 -43
- data/leveldb/db/dbformat.cc +31 -0
- data/leveldb/db/dbformat.h +51 -1
- data/leveldb/db/filename.h +1 -1
- data/leveldb/db/log_format.h +1 -1
- data/leveldb/db/log_reader.cc +16 -11
- data/leveldb/db/memtable.cc +37 -0
- data/leveldb/db/memtable.h +6 -0
- data/leveldb/db/repair.cc +17 -14
- data/leveldb/db/skiplist_test.cc +2 -2
- data/leveldb/db/version_edit.cc +7 -9
- data/leveldb/db/version_edit.h +2 -1
- data/leveldb/db/version_set.cc +416 -104
- data/leveldb/db/version_set.h +78 -14
- data/leveldb/db/version_set_test.cc +179 -0
- data/leveldb/db/write_batch_internal.h +2 -0
- data/leveldb/include/leveldb/c.h +246 -0
- data/leveldb/include/leveldb/db.h +14 -2
- data/leveldb/include/leveldb/env.h +31 -10
- data/leveldb/include/leveldb/options.h +7 -18
- data/leveldb/include/leveldb/slice.h +2 -2
- data/leveldb/include/leveldb/status.h +1 -1
- data/leveldb/port/atomic_pointer.h +144 -0
- data/leveldb/port/port.h +0 -2
- data/leveldb/port/port_android.h +7 -1
- data/leveldb/port/port_example.h +11 -1
- data/leveldb/port/port_posix.h +56 -38
- data/leveldb/table/format.cc +12 -8
- data/leveldb/table/table_test.cc +16 -7
- data/leveldb/util/cache.cc +173 -100
- data/leveldb/util/cache_test.cc +28 -11
- data/leveldb/util/coding.h +4 -4
- data/leveldb/util/comparator.cc +1 -0
- data/leveldb/util/env.cc +10 -5
- data/leveldb/util/env_posix.cc +48 -87
- data/leveldb/util/histogram.cc +11 -0
- data/leveldb/util/histogram.h +1 -0
- data/leveldb/util/posix_logger.h +98 -0
- data/leveldb/util/testharness.cc +12 -0
- data/leveldb/util/testharness.h +10 -1
- data/lib/leveldb.rb +11 -3
- 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()
|
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::
|
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
|
-
|
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(!
|
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
|
-
|
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
|
|
data/leveldb/db/db_bench.cc
CHANGED
@@ -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(
|
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("/
|
391
|
+
Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]);
|
325
392
|
}
|
326
393
|
}
|
327
394
|
if (!FLAGS_use_existing_db) {
|
328
|
-
DestroyDB(
|
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
|
-
|
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
|
-
|
432
|
+
fresh_db = true;
|
433
|
+
method = &Benchmark::WriteSeq;
|
359
434
|
} else if (name == Slice("fillbatch")) {
|
360
|
-
|
435
|
+
fresh_db = true;
|
436
|
+
entries_per_batch_ = 1000;
|
437
|
+
method = &Benchmark::WriteSeq;
|
361
438
|
} else if (name == Slice("fillrandom")) {
|
362
|
-
|
439
|
+
fresh_db = true;
|
440
|
+
method = &Benchmark::WriteRandom;
|
363
441
|
} else if (name == Slice("overwrite")) {
|
364
|
-
|
442
|
+
fresh_db = false;
|
443
|
+
method = &Benchmark::WriteRandom;
|
365
444
|
} else if (name == Slice("fillsync")) {
|
366
|
-
|
367
|
-
|
445
|
+
fresh_db = true;
|
446
|
+
num_ /= 1000;
|
447
|
+
write_options_.sync = true;
|
448
|
+
method = &Benchmark::WriteRandom;
|
368
449
|
} else if (name == Slice("fill100K")) {
|
369
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
404
|
-
|
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
|
-
|
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
|
-
|
424
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
463
|
-
|
639
|
+
thread->stats.AddMessage(buf);
|
640
|
+
thread->stats.AddBytes(bytes);
|
464
641
|
}
|
465
642
|
}
|
466
643
|
|
467
|
-
void SnappyUncompress() {
|
468
|
-
|
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
|
-
|
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
|
-
|
476
|
-
bytes +=
|
477
|
-
FinishedSingleOp();
|
653
|
+
uncompressed);
|
654
|
+
bytes += input.size();
|
655
|
+
thread->stats.FinishedSingleOp();
|
478
656
|
}
|
657
|
+
delete[] uncompressed;
|
479
658
|
|
480
659
|
if (!ok) {
|
481
|
-
|
660
|
+
thread->stats.AddMessage("(snappy failure)");
|
482
661
|
} else {
|
483
|
-
|
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,
|
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
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
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
|
-
|
687
|
+
void DoWrite(ThreadState* thread, bool seq) {
|
688
|
+
if (num_ != FLAGS_num) {
|
515
689
|
char msg[100];
|
516
|
-
snprintf(msg, sizeof(msg), "(%d ops)",
|
517
|
-
|
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
|
-
|
523
|
-
for (int i = 0; i <
|
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 <
|
526
|
-
const int k =
|
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,
|
530
|
-
|
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(
|
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
|
-
|
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
|
-
|
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 =
|
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 =
|
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
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
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
|
-
|
601
|
-
|
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
|
-
|
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), "/
|
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
|
-
|
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
|
-
|
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);
|