Home | History | Annotate | Download | only in bench
      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 }
    719