Add Google's LevelDB support
[novacoin.git] / src / leveldb / doc / bench / db_bench_sqlite3.cc
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 <sqlite3.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 //   fillseqsync   -- write N/100 values in sequential key order in sync mode
17 //   fillseqbatch  -- batch write N values in sequential key order in async mode
18 //   fillrandom    -- write N values in random key order in async mode
19 //   fillrandsync  -- write N/100 values in random key order in sync mode
20 //   fillrandbatch -- batch write N values in sequential key order in async mode
21 //   overwrite     -- overwrite N values in random key order in async mode
22 //   fillrand100K  -- write N/1000 100K values in random order in async mode
23 //   fillseq100K   -- write N/1000 100K values in sequential order in async mode
24 //   readseq       -- read N times sequentially
25 //   readrandom    -- read N times in random order
26 //   readrand100K  -- read N/1000 100K values in sequential order in async mode
27 static const char* FLAGS_benchmarks =
28     "fillseq,"
29     "fillseqsync,"
30     "fillseqbatch,"
31     "fillrandom,"
32     "fillrandsync,"
33     "fillrandbatch,"
34     "overwrite,"
35     "overwritebatch,"
36     "readrandom,"
37     "readseq,"
38     "fillrand100K,"
39     "fillseq100K,"
40     "readseq,"
41     "readrand100K,"
42     ;
43
44 // Number of key/values to place in database
45 static int FLAGS_num = 1000000;
46
47 // Number of read operations to do.  If negative, do FLAGS_num reads.
48 static int FLAGS_reads = -1;
49
50 // Size of each value
51 static int FLAGS_value_size = 100;
52
53 // Print histogram of operation timings
54 static bool FLAGS_histogram = false;
55
56 // Arrange to generate values that shrink to this fraction of
57 // their original size after compression
58 static double FLAGS_compression_ratio = 0.5;
59
60 // Page size. Default 1 KB.
61 static int FLAGS_page_size = 1024;
62
63 // Number of pages.
64 // Default cache size = FLAGS_page_size * FLAGS_num_pages = 4 MB.
65 static int FLAGS_num_pages = 4096;
66
67 // If true, do not destroy the existing database.  If you set this
68 // flag and also specify a benchmark that wants a fresh database, that
69 // benchmark will fail.
70 static bool FLAGS_use_existing_db = false;
71
72 // If true, we allow batch writes to occur
73 static bool FLAGS_transaction = true;
74
75 // If true, we enable Write-Ahead Logging
76 static bool FLAGS_WAL_enabled = true;
77
78 // Use the db with the following name.
79 static const char* FLAGS_db = NULL;
80
81 inline
82 static void ExecErrorCheck(int status, char *err_msg) {
83   if (status != SQLITE_OK) {
84     fprintf(stderr, "SQL error: %s\n", err_msg);
85     sqlite3_free(err_msg);
86     exit(1);
87   }
88 }
89
90 inline
91 static void StepErrorCheck(int status) {
92   if (status != SQLITE_DONE) {
93     fprintf(stderr, "SQL step error: status = %d\n", status);
94     exit(1);
95   }
96 }
97
98 inline
99 static void ErrorCheck(int status) {
100   if (status != SQLITE_OK) {
101     fprintf(stderr, "sqlite3 error: status = %d\n", status);
102     exit(1);
103   }
104 }
105
106 inline
107 static void WalCheckpoint(sqlite3* db_) {
108   // Flush all writes to disk
109   if (FLAGS_WAL_enabled) {
110     sqlite3_wal_checkpoint_v2(db_, NULL, SQLITE_CHECKPOINT_FULL, NULL, NULL);
111   }
112 }
113
114 namespace leveldb {
115
116 // Helper for quickly generating random data.
117 namespace {
118 class RandomGenerator {
119  private:
120   std::string data_;
121   int pos_;
122
123  public:
124   RandomGenerator() {
125     // We use a limited amount of data over and over again and ensure
126     // that it is larger than the compression window (32KB), and also
127     // large enough to serve all typical value sizes we want to write.
128     Random rnd(301);
129     std::string piece;
130     while (data_.size() < 1048576) {
131       // Add a short fragment that is as compressible as specified
132       // by FLAGS_compression_ratio.
133       test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
134       data_.append(piece);
135     }
136     pos_ = 0;
137   }
138
139   Slice Generate(int len) {
140     if (pos_ + len > data_.size()) {
141       pos_ = 0;
142       assert(len < data_.size());
143     }
144     pos_ += len;
145     return Slice(data_.data() + pos_ - len, len);
146   }
147 };
148
149 static Slice TrimSpace(Slice s) {
150   int start = 0;
151   while (start < s.size() && isspace(s[start])) {
152     start++;
153   }
154   int limit = s.size();
155   while (limit > start && isspace(s[limit-1])) {
156     limit--;
157   }
158   return Slice(s.data() + start, limit - start);
159 }
160
161 }  // namespace
162
163 class Benchmark {
164  private:
165   sqlite3* db_;
166   int db_num_;
167   int num_;
168   int reads_;
169   double start_;
170   double last_op_finish_;
171   int64_t bytes_;
172   std::string message_;
173   Histogram hist_;
174   RandomGenerator gen_;
175   Random rand_;
176
177   // State kept for progress messages
178   int done_;
179   int next_report_;     // When to report next
180
181   void PrintHeader() {
182     const int kKeySize = 16;
183     PrintEnvironment();
184     fprintf(stdout, "Keys:       %d bytes each\n", kKeySize);
185     fprintf(stdout, "Values:     %d bytes each\n", FLAGS_value_size);
186     fprintf(stdout, "Entries:    %d\n", num_);
187     fprintf(stdout, "RawSize:    %.1f MB (estimated)\n",
188             ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_)
189              / 1048576.0));
190     PrintWarnings();
191     fprintf(stdout, "------------------------------------------------\n");
192   }
193
194   void PrintWarnings() {
195 #if defined(__GNUC__) && !defined(__OPTIMIZE__)
196     fprintf(stdout,
197             "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"
198             );
199 #endif
200 #ifndef NDEBUG
201     fprintf(stdout,
202             "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
203 #endif
204   }
205
206   void PrintEnvironment() {
207     fprintf(stderr, "SQLite:     version %s\n", SQLITE_VERSION);
208
209 #if defined(__linux)
210     time_t now = time(NULL);
211     fprintf(stderr, "Date:       %s", ctime(&now));  // ctime() adds newline
212
213     FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
214     if (cpuinfo != NULL) {
215       char line[1000];
216       int num_cpus = 0;
217       std::string cpu_type;
218       std::string cache_size;
219       while (fgets(line, sizeof(line), cpuinfo) != NULL) {
220         const char* sep = strchr(line, ':');
221         if (sep == NULL) {
222           continue;
223         }
224         Slice key = TrimSpace(Slice(line, sep - 1 - line));
225         Slice val = TrimSpace(Slice(sep + 1));
226         if (key == "model name") {
227           ++num_cpus;
228           cpu_type = val.ToString();
229         } else if (key == "cache size") {
230           cache_size = val.ToString();
231         }
232       }
233       fclose(cpuinfo);
234       fprintf(stderr, "CPU:        %d * %s\n", num_cpus, cpu_type.c_str());
235       fprintf(stderr, "CPUCache:   %s\n", cache_size.c_str());
236     }
237 #endif
238   }
239
240   void Start() {
241     start_ = Env::Default()->NowMicros() * 1e-6;
242     bytes_ = 0;
243     message_.clear();
244     last_op_finish_ = start_;
245     hist_.Clear();
246     done_ = 0;
247     next_report_ = 100;
248   }
249
250   void FinishedSingleOp() {
251     if (FLAGS_histogram) {
252       double now = Env::Default()->NowMicros() * 1e-6;
253       double micros = (now - last_op_finish_) * 1e6;
254       hist_.Add(micros);
255       if (micros > 20000) {
256         fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
257         fflush(stderr);
258       }
259       last_op_finish_ = now;
260     }
261
262     done_++;
263     if (done_ >= next_report_) {
264       if      (next_report_ < 1000)   next_report_ += 100;
265       else if (next_report_ < 5000)   next_report_ += 500;
266       else if (next_report_ < 10000)  next_report_ += 1000;
267       else if (next_report_ < 50000)  next_report_ += 5000;
268       else if (next_report_ < 100000) next_report_ += 10000;
269       else if (next_report_ < 500000) next_report_ += 50000;
270       else                            next_report_ += 100000;
271       fprintf(stderr, "... finished %d ops%30s\r", done_, "");
272       fflush(stderr);
273     }
274   }
275
276   void Stop(const Slice& name) {
277     double finish = Env::Default()->NowMicros() * 1e-6;
278
279     // Pretend at least one op was done in case we are running a benchmark
280     // that does not call FinishedSingleOp().
281     if (done_ < 1) done_ = 1;
282
283     if (bytes_ > 0) {
284       char rate[100];
285       snprintf(rate, sizeof(rate), "%6.1f MB/s",
286                (bytes_ / 1048576.0) / (finish - start_));
287       if (!message_.empty()) {
288         message_  = std::string(rate) + " " + message_;
289       } else {
290         message_ = rate;
291       }
292     }
293
294     fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
295             name.ToString().c_str(),
296             (finish - start_) * 1e6 / done_,
297             (message_.empty() ? "" : " "),
298             message_.c_str());
299     if (FLAGS_histogram) {
300       fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
301     }
302     fflush(stdout);
303   }
304
305  public:
306   enum Order {
307     SEQUENTIAL,
308     RANDOM
309   };
310   enum DBState {
311     FRESH,
312     EXISTING
313   };
314
315   Benchmark()
316   : db_(NULL),
317     db_num_(0),
318     num_(FLAGS_num),
319     reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
320     bytes_(0),
321     rand_(301) {
322     std::vector<std::string> files;
323     std::string test_dir;
324     Env::Default()->GetTestDirectory(&test_dir);
325     Env::Default()->GetChildren(test_dir, &files);
326     if (!FLAGS_use_existing_db) {
327       for (int i = 0; i < files.size(); i++) {
328         if (Slice(files[i]).starts_with("dbbench_sqlite3")) {
329           std::string file_name(test_dir);
330           file_name += "/";
331           file_name += files[i];
332           Env::Default()->DeleteFile(file_name.c_str());
333         }
334       }
335     }
336   }
337
338   ~Benchmark() {
339     int status = sqlite3_close(db_);
340     ErrorCheck(status);
341   }
342
343   void Run() {
344     PrintHeader();
345     Open();
346
347     const char* benchmarks = FLAGS_benchmarks;
348     while (benchmarks != NULL) {
349       const char* sep = strchr(benchmarks, ',');
350       Slice name;
351       if (sep == NULL) {
352         name = benchmarks;
353         benchmarks = NULL;
354       } else {
355         name = Slice(benchmarks, sep - benchmarks);
356         benchmarks = sep + 1;
357       }
358
359       bytes_ = 0;
360       Start();
361
362       bool known = true;
363       bool write_sync = false;
364       if (name == Slice("fillseq")) {
365         Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1);
366         WalCheckpoint(db_);
367       } else if (name == Slice("fillseqbatch")) {
368         Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1000);
369         WalCheckpoint(db_);
370       } else if (name == Slice("fillrandom")) {
371         Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1);
372         WalCheckpoint(db_);
373       } else if (name == Slice("fillrandbatch")) {
374         Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1000);
375         WalCheckpoint(db_);
376       } else if (name == Slice("overwrite")) {
377         Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1);
378         WalCheckpoint(db_);
379       } else if (name == Slice("overwritebatch")) {
380         Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1000);
381         WalCheckpoint(db_);
382       } else if (name == Slice("fillrandsync")) {
383         write_sync = true;
384         Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1);
385         WalCheckpoint(db_);
386       } else if (name == Slice("fillseqsync")) {
387         write_sync = true;
388         Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1);
389         WalCheckpoint(db_);
390       } else if (name == Slice("fillrand100K")) {
391         Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1);
392         WalCheckpoint(db_);
393       } else if (name == Slice("fillseq100K")) {
394         Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1);
395         WalCheckpoint(db_);
396       } else if (name == Slice("readseq")) {
397         ReadSequential();
398       } else if (name == Slice("readrandom")) {
399         Read(RANDOM, 1);
400       } else if (name == Slice("readrand100K")) {
401         int n = reads_;
402         reads_ /= 1000;
403         Read(RANDOM, 1);
404         reads_ = n;
405       } else {
406         known = false;
407         if (name != Slice()) {  // No error message for empty name
408           fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
409         }
410       }
411       if (known) {
412         Stop(name);
413       }
414     }
415   }
416
417   void Open() {
418     assert(db_ == NULL);
419
420     int status;
421     char file_name[100];
422     char* err_msg = NULL;
423     db_num_++;
424
425     // Open database
426     std::string tmp_dir;
427     Env::Default()->GetTestDirectory(&tmp_dir);
428     snprintf(file_name, sizeof(file_name),
429              "%s/dbbench_sqlite3-%d.db",
430              tmp_dir.c_str(),
431              db_num_);
432     status = sqlite3_open(file_name, &db_);
433     if (status) {
434       fprintf(stderr, "open error: %s\n", sqlite3_errmsg(db_));
435       exit(1);
436     }
437
438     // Change SQLite cache size
439     char cache_size[100];
440     snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d",
441              FLAGS_num_pages);
442     status = sqlite3_exec(db_, cache_size, NULL, NULL, &err_msg);
443     ExecErrorCheck(status, err_msg);
444
445     // FLAGS_page_size is defaulted to 1024
446     if (FLAGS_page_size != 1024) {
447       char page_size[100];
448       snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d",
449                FLAGS_page_size);
450       status = sqlite3_exec(db_, page_size, NULL, NULL, &err_msg);
451       ExecErrorCheck(status, err_msg);
452     }
453
454     // Change journal mode to WAL if WAL enabled flag is on
455     if (FLAGS_WAL_enabled) {
456       std::string WAL_stmt = "PRAGMA journal_mode = WAL";
457
458       // LevelDB's default cache size is a combined 4 MB
459       std::string WAL_checkpoint = "PRAGMA wal_autocheckpoint = 4096";
460       status = sqlite3_exec(db_, WAL_stmt.c_str(), NULL, NULL, &err_msg);
461       ExecErrorCheck(status, err_msg);
462       status = sqlite3_exec(db_, WAL_checkpoint.c_str(), NULL, NULL, &err_msg);
463       ExecErrorCheck(status, err_msg);
464     }
465
466     // Change locking mode to exclusive and create tables/index for database
467     std::string locking_stmt = "PRAGMA locking_mode = EXCLUSIVE";
468     std::string create_stmt =
469           "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))";
470     std::string stmt_array[] = { locking_stmt, create_stmt };
471     int stmt_array_length = sizeof(stmt_array) / sizeof(std::string);
472     for (int i = 0; i < stmt_array_length; i++) {
473       status = sqlite3_exec(db_, stmt_array[i].c_str(), NULL, NULL, &err_msg);
474       ExecErrorCheck(status, err_msg);
475     }
476   }
477
478   void Write(bool write_sync, Order order, DBState state,
479              int num_entries, int value_size, int entries_per_batch) {
480     // Create new database if state == FRESH
481     if (state == FRESH) {
482       if (FLAGS_use_existing_db) {
483         message_ = "skipping (--use_existing_db is true)";
484         return;
485       }
486       sqlite3_close(db_);
487       db_ = NULL;
488       Open();
489       Start();
490     }
491
492     if (num_entries != num_) {
493       char msg[100];
494       snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
495       message_ = msg;
496     }
497
498     char* err_msg = NULL;
499     int status;
500
501     sqlite3_stmt *replace_stmt, *begin_trans_stmt, *end_trans_stmt;
502     std::string replace_str = "REPLACE INTO test (key, value) VALUES (?, ?)";
503     std::string begin_trans_str = "BEGIN TRANSACTION;";
504     std::string end_trans_str = "END TRANSACTION;";
505
506     // Check for synchronous flag in options
507     std::string sync_stmt = (write_sync) ? "PRAGMA synchronous = FULL" :
508                                            "PRAGMA synchronous = OFF";
509     status = sqlite3_exec(db_, sync_stmt.c_str(), NULL, NULL, &err_msg);
510     ExecErrorCheck(status, err_msg);
511
512     // Preparing sqlite3 statements
513     status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1,
514                                 &replace_stmt, NULL);
515     ErrorCheck(status);
516     status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1,
517                                 &begin_trans_stmt, NULL);
518     ErrorCheck(status);
519     status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1,
520                                 &end_trans_stmt, NULL);
521     ErrorCheck(status);
522
523     bool transaction = (entries_per_batch > 1);
524     for (int i = 0; i < num_entries; i += entries_per_batch) {
525       // Begin write transaction
526       if (FLAGS_transaction && transaction) {
527         status = sqlite3_step(begin_trans_stmt);
528         StepErrorCheck(status);
529         status = sqlite3_reset(begin_trans_stmt);
530         ErrorCheck(status);
531       }
532
533       // Create and execute SQL statements
534       for (int j = 0; j < entries_per_batch; j++) {
535         const char* value = gen_.Generate(value_size).data();
536
537         // Create values for key-value pair
538         const int k = (order == SEQUENTIAL) ? i + j :
539                       (rand_.Next() % num_entries);
540         char key[100];
541         snprintf(key, sizeof(key), "%016d", k);
542
543         // Bind KV values into replace_stmt
544         status = sqlite3_bind_blob(replace_stmt, 1, key, 16, SQLITE_STATIC);
545         ErrorCheck(status);
546         status = sqlite3_bind_blob(replace_stmt, 2, value,
547                                    value_size, SQLITE_STATIC);
548         ErrorCheck(status);
549
550         // Execute replace_stmt
551         bytes_ += value_size + strlen(key);
552         status = sqlite3_step(replace_stmt);
553         StepErrorCheck(status);
554
555         // Reset SQLite statement for another use
556         status = sqlite3_clear_bindings(replace_stmt);
557         ErrorCheck(status);
558         status = sqlite3_reset(replace_stmt);
559         ErrorCheck(status);
560
561         FinishedSingleOp();
562       }
563
564       // End write transaction
565       if (FLAGS_transaction && transaction) {
566         status = sqlite3_step(end_trans_stmt);
567         StepErrorCheck(status);
568         status = sqlite3_reset(end_trans_stmt);
569         ErrorCheck(status);
570       }
571     }
572
573     status = sqlite3_finalize(replace_stmt);
574     ErrorCheck(status);
575     status = sqlite3_finalize(begin_trans_stmt);
576     ErrorCheck(status);
577     status = sqlite3_finalize(end_trans_stmt);
578     ErrorCheck(status);
579   }
580
581   void Read(Order order, int entries_per_batch) {
582     int status;
583     sqlite3_stmt *read_stmt, *begin_trans_stmt, *end_trans_stmt;
584
585     std::string read_str = "SELECT * FROM test WHERE key = ?";
586     std::string begin_trans_str = "BEGIN TRANSACTION;";
587     std::string end_trans_str = "END TRANSACTION;";
588
589     // Preparing sqlite3 statements
590     status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1,
591                                 &begin_trans_stmt, NULL);
592     ErrorCheck(status);
593     status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1,
594                                 &end_trans_stmt, NULL);
595     ErrorCheck(status);
596     status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, NULL);
597     ErrorCheck(status);
598
599     bool transaction = (entries_per_batch > 1);
600     for (int i = 0; i < reads_; i += entries_per_batch) {
601       // Begin read transaction
602       if (FLAGS_transaction && transaction) {
603         status = sqlite3_step(begin_trans_stmt);
604         StepErrorCheck(status);
605         status = sqlite3_reset(begin_trans_stmt);
606         ErrorCheck(status);
607       }
608
609       // Create and execute SQL statements
610       for (int j = 0; j < entries_per_batch; j++) {
611         // Create key value
612         char key[100];
613         int k = (order == SEQUENTIAL) ? i + j : (rand_.Next() % reads_);
614         snprintf(key, sizeof(key), "%016d", k);
615
616         // Bind key value into read_stmt
617         status = sqlite3_bind_blob(read_stmt, 1, key, 16, SQLITE_STATIC);
618         ErrorCheck(status);
619
620         // Execute read statement
621         while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) {}
622         StepErrorCheck(status);
623
624         // Reset SQLite statement for another use
625         status = sqlite3_clear_bindings(read_stmt);
626         ErrorCheck(status);
627         status = sqlite3_reset(read_stmt);
628         ErrorCheck(status);
629         FinishedSingleOp();
630       }
631
632       // End read transaction
633       if (FLAGS_transaction && transaction) {
634         status = sqlite3_step(end_trans_stmt);
635         StepErrorCheck(status);
636         status = sqlite3_reset(end_trans_stmt);
637         ErrorCheck(status);
638       }
639     }
640
641     status = sqlite3_finalize(read_stmt);
642     ErrorCheck(status);
643     status = sqlite3_finalize(begin_trans_stmt);
644     ErrorCheck(status);
645     status = sqlite3_finalize(end_trans_stmt);
646     ErrorCheck(status);
647   }
648
649   void ReadSequential() {
650     int status;
651     sqlite3_stmt *pStmt;
652     std::string read_str = "SELECT * FROM test ORDER BY key";
653
654     status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, NULL);
655     ErrorCheck(status);
656     for (int i = 0; i < reads_ && SQLITE_ROW == sqlite3_step(pStmt); i++) {
657       bytes_ += sqlite3_column_bytes(pStmt, 1) + sqlite3_column_bytes(pStmt, 2);
658       FinishedSingleOp();
659     }
660
661     status = sqlite3_finalize(pStmt);
662     ErrorCheck(status);
663   }
664
665 };
666
667 }  // namespace leveldb
668
669 int main(int argc, char** argv) {
670   std::string default_db_path;
671   for (int i = 1; i < argc; i++) {
672     double d;
673     int n;
674     char junk;
675     if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
676       FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
677     } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
678                (n == 0 || n == 1)) {
679       FLAGS_histogram = n;
680     } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
681       FLAGS_compression_ratio = d;
682     } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 &&
683                (n == 0 || n == 1)) {
684       FLAGS_use_existing_db = n;
685     } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
686       FLAGS_num = n;
687     } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
688       FLAGS_reads = n;
689     } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
690       FLAGS_value_size = n;
691     } else if (leveldb::Slice(argv[i]) == leveldb::Slice("--no_transaction")) {
692       FLAGS_transaction = false;
693     } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) {
694       FLAGS_page_size = n;
695     } else if (sscanf(argv[i], "--num_pages=%d%c", &n, &junk) == 1) {
696       FLAGS_num_pages = n;
697     } else if (sscanf(argv[i], "--WAL_enabled=%d%c", &n, &junk) == 1 &&
698                (n == 0 || n == 1)) {
699       FLAGS_WAL_enabled = n;
700     } else if (strncmp(argv[i], "--db=", 5) == 0) {
701       FLAGS_db = argv[i] + 5;
702     } else {
703       fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
704       exit(1);
705     }
706   }
707
708   // Choose a location for the test database if none given with --db=<path>
709   if (FLAGS_db == NULL) {
710       leveldb::Env::Default()->GetTestDirectory(&default_db_path);
711       default_db_path += "/dbbench";
712       FLAGS_db = default_db_path.c_str();
713   }
714
715   leveldb::Benchmark benchmark;
716   benchmark.Run();
717   return 0;
718 }