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