You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

714 lines
22 KiB

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