作者: 韩晨旭 10225101440 李畅 10225102463
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.

726 lines
23 KiB

Add WITHOUT ROWID to SQLite benchmark. The SQLite-specific schema feature is documented at https://www.sqlite.org/withoutrowid.html and https://www.sqlite.org/rowidtable.html. By default, SQLite stores each table in a B-tree keyed by an integer, called the ROWID. Any index, including the PRIMARY KEY index, is a separate B-tree mapping index keys to ROWIDs. Tables without ROWIDs are stored in a B-tree keyed by the primary key. Additional indexes (the PRIMARY KEY index is implicitly built into the table) are stored as B-trees mapping index keys to row primary keys. This CL introduces a boolean --use-rowids flag to db_bench_sqlite. When the flag is false (default), the schema of the test table includes WITHOUT ROWID. The test table uses a primary key, so adding WITHOUT ROWID to the schema reduces the number of B-trees used by the benchmark from 2 to 1. This brings SQLite's disk usage closer to LevelDB. When WITHOUT ROWID is used, SQLite fares better (than today) on benchmarks with small (16-byte) keys, and worse on benchmarks with large (100kb) keys. Baseline results: fillseq : 21.310 micros/op; 5.2 MB/s fillseqsync : 146.377 micros/op; 0.8 MB/s (10000 ops) fillseqbatch : 2.065 micros/op; 53.6 MB/s fillrandom : 34.767 micros/op; 3.2 MB/s fillrandsync : 159.943 micros/op; 0.7 MB/s (10000 ops) fillrandbatch : 15.055 micros/op; 7.3 MB/s overwrite : 43.660 micros/op; 2.5 MB/s overwritebatch : 27.691 micros/op; 4.0 MB/s readrandom : 12.725 micros/op; readseq : 2.602 micros/op; 36.7 MB/s fillrand100K : 606.333 micros/op; 157.3 MB/s (1000 ops) fillseq100K : 657.457 micros/op; 145.1 MB/s (1000 ops) readseq : 46.523 micros/op; 2049.9 MB/s readrand100K : 54.943 micros/op; Results after this CL: fillseq : 16.231 micros/op; 6.8 MB/s fillseqsync : 147.460 micros/op; 0.8 MB/s (10000 ops) fillseqbatch : 2.294 micros/op; 48.2 MB/s fillrandom : 27.871 micros/op; 4.0 MB/s fillrandsync : 141.979 micros/op; 0.8 MB/s (10000 ops) fillrandbatch : 16.087 micros/op; 6.9 MB/s overwrite : 26.829 micros/op; 4.1 MB/s overwritebatch : 19.014 micros/op; 5.8 MB/s readrandom : 11.657 micros/op; readseq : 0.155 micros/op; 615.0 MB/s fillrand100K : 816.812 micros/op; 116.8 MB/s (1000 ops) fillseq100K : 754.689 micros/op; 126.4 MB/s (1000 ops) readseq : 47.112 micros/op; 2024.3 MB/s readrand100K : 287.679 micros/op; Results after this CL, with --use-rowids=1 fillseq : 20.655 micros/op; 5.4 MB/s fillseqsync : 146.408 micros/op; 0.8 MB/s (10000 ops) fillseqbatch : 2.045 micros/op; 54.1 MB/s fillrandom : 34.080 micros/op; 3.2 MB/s fillrandsync : 154.582 micros/op; 0.7 MB/s (10000 ops) fillrandbatch : 14.404 micros/op; 7.7 MB/s overwrite : 42.928 micros/op; 2.6 MB/s overwritebatch : 27.829 micros/op; 4.0 MB/s readrandom : 12.835 micros/op; readseq : 2.483 micros/op; 38.4 MB/s fillrand100K : 603.265 micros/op; 158.1 MB/s (1000 ops) fillseq100K : 662.473 micros/op; 144.0 MB/s (1000 ops) readseq : 45.478 micros/op; 2097.0 MB/s readrand100K : 54.439 micros/op; PiperOrigin-RevId: 283407101
5 years ago
Add Env::Remove{File,Dir} which obsolete Env::Delete{File,Dir}. The "DeleteFile" method name causes pain for Windows developers, because <windows.h> #defines a DeleteFile macro to DeleteFileW or DeleteFileA. Current code uses workarounds, like #undefining DeleteFile everywhere an Env is declared, implemented, or used. This CL removes the need for workarounds by renaming Env::DeleteFile to Env::RemoveFile. For consistency, Env::DeleteDir is also renamed to Env::RemoveDir. A few internal methods are also renamed for consistency. Software that supports Windows is expected to migrate any Env implementations and usage to Remove{File,Dir}, and never use the name Env::Delete{File,Dir} in its code. The renaming is done in a backwards-compatible way, at the risk of making it slightly more difficult to build a new correct Env implementation. The backwards compatibility is achieved using the following hacks: 1) Env::Remove{File,Dir} methods are added, with a default implementation that calls into Env::Delete{File,Dir}. This makes old Env implementations compatible with code that calls into the updated API. 2) The Env::Delete{File,Dir} methods are no longer pure virtuals. Instead, they gain a default implementation that calls into Env::Remove{File,Dir}. This makes updated Env implementations compatible with code that calls into the old API. The cost of this approach is that it's possible to write an Env without overriding either Rename{File,Dir} or Delete{File,Dir}, without getting a compiler warning. However, attempting to run the test suite will immediately fail with an infinite call stack ending in {Remove,Delete}{File,Dir}, making developers aware of the problem. PiperOrigin-RevId: 288710907
5 years ago
Add WITHOUT ROWID to SQLite benchmark. The SQLite-specific schema feature is documented at https://www.sqlite.org/withoutrowid.html and https://www.sqlite.org/rowidtable.html. By default, SQLite stores each table in a B-tree keyed by an integer, called the ROWID. Any index, including the PRIMARY KEY index, is a separate B-tree mapping index keys to ROWIDs. Tables without ROWIDs are stored in a B-tree keyed by the primary key. Additional indexes (the PRIMARY KEY index is implicitly built into the table) are stored as B-trees mapping index keys to row primary keys. This CL introduces a boolean --use-rowids flag to db_bench_sqlite. When the flag is false (default), the schema of the test table includes WITHOUT ROWID. The test table uses a primary key, so adding WITHOUT ROWID to the schema reduces the number of B-trees used by the benchmark from 2 to 1. This brings SQLite's disk usage closer to LevelDB. When WITHOUT ROWID is used, SQLite fares better (than today) on benchmarks with small (16-byte) keys, and worse on benchmarks with large (100kb) keys. Baseline results: fillseq : 21.310 micros/op; 5.2 MB/s fillseqsync : 146.377 micros/op; 0.8 MB/s (10000 ops) fillseqbatch : 2.065 micros/op; 53.6 MB/s fillrandom : 34.767 micros/op; 3.2 MB/s fillrandsync : 159.943 micros/op; 0.7 MB/s (10000 ops) fillrandbatch : 15.055 micros/op; 7.3 MB/s overwrite : 43.660 micros/op; 2.5 MB/s overwritebatch : 27.691 micros/op; 4.0 MB/s readrandom : 12.725 micros/op; readseq : 2.602 micros/op; 36.7 MB/s fillrand100K : 606.333 micros/op; 157.3 MB/s (1000 ops) fillseq100K : 657.457 micros/op; 145.1 MB/s (1000 ops) readseq : 46.523 micros/op; 2049.9 MB/s readrand100K : 54.943 micros/op; Results after this CL: fillseq : 16.231 micros/op; 6.8 MB/s fillseqsync : 147.460 micros/op; 0.8 MB/s (10000 ops) fillseqbatch : 2.294 micros/op; 48.2 MB/s fillrandom : 27.871 micros/op; 4.0 MB/s fillrandsync : 141.979 micros/op; 0.8 MB/s (10000 ops) fillrandbatch : 16.087 micros/op; 6.9 MB/s overwrite : 26.829 micros/op; 4.1 MB/s overwritebatch : 19.014 micros/op; 5.8 MB/s readrandom : 11.657 micros/op; readseq : 0.155 micros/op; 615.0 MB/s fillrand100K : 816.812 micros/op; 116.8 MB/s (1000 ops) fillseq100K : 754.689 micros/op; 126.4 MB/s (1000 ops) readseq : 47.112 micros/op; 2024.3 MB/s readrand100K : 287.679 micros/op; Results after this CL, with --use-rowids=1 fillseq : 20.655 micros/op; 5.4 MB/s fillseqsync : 146.408 micros/op; 0.8 MB/s (10000 ops) fillseqbatch : 2.045 micros/op; 54.1 MB/s fillrandom : 34.080 micros/op; 3.2 MB/s fillrandsync : 154.582 micros/op; 0.7 MB/s (10000 ops) fillrandbatch : 14.404 micros/op; 7.7 MB/s overwrite : 42.928 micros/op; 2.6 MB/s overwritebatch : 27.829 micros/op; 4.0 MB/s readrandom : 12.835 micros/op; readseq : 2.483 micros/op; 38.4 MB/s fillrand100K : 603.265 micros/op; 158.1 MB/s (1000 ops) fillseq100K : 662.473 micros/op; 144.0 MB/s (1000 ops) readseq : 45.478 micros/op; 2097.0 MB/s readrand100K : 54.439 micros/op; PiperOrigin-RevId: 283407101
5 years ago
Add WITHOUT ROWID to SQLite benchmark. The SQLite-specific schema feature is documented at https://www.sqlite.org/withoutrowid.html and https://www.sqlite.org/rowidtable.html. By default, SQLite stores each table in a B-tree keyed by an integer, called the ROWID. Any index, including the PRIMARY KEY index, is a separate B-tree mapping index keys to ROWIDs. Tables without ROWIDs are stored in a B-tree keyed by the primary key. Additional indexes (the PRIMARY KEY index is implicitly built into the table) are stored as B-trees mapping index keys to row primary keys. This CL introduces a boolean --use-rowids flag to db_bench_sqlite. When the flag is false (default), the schema of the test table includes WITHOUT ROWID. The test table uses a primary key, so adding WITHOUT ROWID to the schema reduces the number of B-trees used by the benchmark from 2 to 1. This brings SQLite's disk usage closer to LevelDB. When WITHOUT ROWID is used, SQLite fares better (than today) on benchmarks with small (16-byte) keys, and worse on benchmarks with large (100kb) keys. Baseline results: fillseq : 21.310 micros/op; 5.2 MB/s fillseqsync : 146.377 micros/op; 0.8 MB/s (10000 ops) fillseqbatch : 2.065 micros/op; 53.6 MB/s fillrandom : 34.767 micros/op; 3.2 MB/s fillrandsync : 159.943 micros/op; 0.7 MB/s (10000 ops) fillrandbatch : 15.055 micros/op; 7.3 MB/s overwrite : 43.660 micros/op; 2.5 MB/s overwritebatch : 27.691 micros/op; 4.0 MB/s readrandom : 12.725 micros/op; readseq : 2.602 micros/op; 36.7 MB/s fillrand100K : 606.333 micros/op; 157.3 MB/s (1000 ops) fillseq100K : 657.457 micros/op; 145.1 MB/s (1000 ops) readseq : 46.523 micros/op; 2049.9 MB/s readrand100K : 54.943 micros/op; Results after this CL: fillseq : 16.231 micros/op; 6.8 MB/s fillseqsync : 147.460 micros/op; 0.8 MB/s (10000 ops) fillseqbatch : 2.294 micros/op; 48.2 MB/s fillrandom : 27.871 micros/op; 4.0 MB/s fillrandsync : 141.979 micros/op; 0.8 MB/s (10000 ops) fillrandbatch : 16.087 micros/op; 6.9 MB/s overwrite : 26.829 micros/op; 4.1 MB/s overwritebatch : 19.014 micros/op; 5.8 MB/s readrandom : 11.657 micros/op; readseq : 0.155 micros/op; 615.0 MB/s fillrand100K : 816.812 micros/op; 116.8 MB/s (1000 ops) fillseq100K : 754.689 micros/op; 126.4 MB/s (1000 ops) readseq : 47.112 micros/op; 2024.3 MB/s readrand100K : 287.679 micros/op; Results after this CL, with --use-rowids=1 fillseq : 20.655 micros/op; 5.4 MB/s fillseqsync : 146.408 micros/op; 0.8 MB/s (10000 ops) fillseqbatch : 2.045 micros/op; 54.1 MB/s fillrandom : 34.080 micros/op; 3.2 MB/s fillrandsync : 154.582 micros/op; 0.7 MB/s (10000 ops) fillrandbatch : 14.404 micros/op; 7.7 MB/s overwrite : 42.928 micros/op; 2.6 MB/s overwritebatch : 27.829 micros/op; 4.0 MB/s readrandom : 12.835 micros/op; readseq : 2.483 micros/op; 38.4 MB/s fillrand100K : 603.265 micros/op; 158.1 MB/s (1000 ops) fillseq100K : 662.473 micros/op; 144.0 MB/s (1000 ops) readseq : 45.478 micros/op; 2097.0 MB/s readrand100K : 54.439 micros/op; PiperOrigin-RevId: 283407101
5 years ago
  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 <cstdio>
  6. #include <cstdlib>
  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, the SQLite table has ROWIDs.
  61. static bool FLAGS_use_rowids = false;
  62. // If true, we allow batch writes to occur
  63. static bool FLAGS_transaction = true;
  64. // If true, we enable Write-Ahead Logging
  65. static bool FLAGS_WAL_enabled = true;
  66. // Use the db with the following name.
  67. static const char* FLAGS_db = nullptr;
  68. inline static void ExecErrorCheck(int status, char* err_msg) {
  69. if (status != SQLITE_OK) {
  70. std::fprintf(stderr, "SQL error: %s\n", err_msg);
  71. sqlite3_free(err_msg);
  72. std::exit(1);
  73. }
  74. }
  75. inline static void StepErrorCheck(int status) {
  76. if (status != SQLITE_DONE) {
  77. std::fprintf(stderr, "SQL step error: status = %d\n", status);
  78. std::exit(1);
  79. }
  80. }
  81. inline static void ErrorCheck(int status) {
  82. if (status != SQLITE_OK) {
  83. std::fprintf(stderr, "sqlite3 error: status = %d\n", status);
  84. std::exit(1);
  85. }
  86. }
  87. inline static void WalCheckpoint(sqlite3* db_) {
  88. // Flush all writes to disk
  89. if (FLAGS_WAL_enabled) {
  90. sqlite3_wal_checkpoint_v2(db_, nullptr, SQLITE_CHECKPOINT_FULL, nullptr,
  91. nullptr);
  92. }
  93. }
  94. namespace leveldb {
  95. // Helper for quickly generating random data.
  96. namespace {
  97. class RandomGenerator {
  98. private:
  99. std::string data_;
  100. int pos_;
  101. public:
  102. RandomGenerator() {
  103. // We use a limited amount of data over and over again and ensure
  104. // that it is larger than the compression window (32KB), and also
  105. // large enough to serve all typical value sizes we want to write.
  106. Random rnd(301);
  107. std::string piece;
  108. while (data_.size() < 1048576) {
  109. // Add a short fragment that is as compressible as specified
  110. // by FLAGS_compression_ratio.
  111. test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
  112. data_.append(piece);
  113. }
  114. pos_ = 0;
  115. }
  116. Slice Generate(int len) {
  117. if (pos_ + len > data_.size()) {
  118. pos_ = 0;
  119. assert(len < data_.size());
  120. }
  121. pos_ += len;
  122. return Slice(data_.data() + pos_ - len, len);
  123. }
  124. };
  125. static Slice TrimSpace(Slice s) {
  126. int start = 0;
  127. while (start < s.size() && isspace(s[start])) {
  128. start++;
  129. }
  130. int limit = s.size();
  131. while (limit > start && isspace(s[limit - 1])) {
  132. limit--;
  133. }
  134. return Slice(s.data() + start, limit - start);
  135. }
  136. } // namespace
  137. class Benchmark {
  138. private:
  139. sqlite3* db_;
  140. int db_num_;
  141. int num_;
  142. int reads_;
  143. double start_;
  144. double last_op_finish_;
  145. int64_t bytes_;
  146. std::string message_;
  147. Histogram hist_;
  148. RandomGenerator gen_;
  149. Random rand_;
  150. // State kept for progress messages
  151. int done_;
  152. int next_report_; // When to report next
  153. void PrintHeader() {
  154. const int kKeySize = 16;
  155. PrintEnvironment();
  156. std::fprintf(stdout, "Keys: %d bytes each\n", kKeySize);
  157. std::fprintf(stdout, "Values: %d bytes each\n", FLAGS_value_size);
  158. std::fprintf(stdout, "Entries: %d\n", num_);
  159. std::fprintf(stdout, "RawSize: %.1f MB (estimated)\n",
  160. ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) /
  161. 1048576.0));
  162. PrintWarnings();
  163. std::fprintf(stdout, "------------------------------------------------\n");
  164. }
  165. void PrintWarnings() {
  166. #if defined(__GNUC__) && !defined(__OPTIMIZE__)
  167. std::fprintf(
  168. stdout,
  169. "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
  170. #endif
  171. #ifndef NDEBUG
  172. std::fprintf(
  173. stdout,
  174. "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
  175. #endif
  176. }
  177. void PrintEnvironment() {
  178. std::fprintf(stderr, "SQLite: version %s\n", SQLITE_VERSION);
  179. #if defined(__linux)
  180. time_t now = time(nullptr);
  181. std::fprintf(stderr, "Date: %s",
  182. ctime(&now)); // ctime() adds newline
  183. FILE* cpuinfo = std::fopen("/proc/cpuinfo", "r");
  184. if (cpuinfo != nullptr) {
  185. char line[1000];
  186. int num_cpus = 0;
  187. std::string cpu_type;
  188. std::string cache_size;
  189. while (fgets(line, sizeof(line), cpuinfo) != nullptr) {
  190. const char* sep = strchr(line, ':');
  191. if (sep == nullptr) {
  192. continue;
  193. }
  194. Slice key = TrimSpace(Slice(line, sep - 1 - line));
  195. Slice val = TrimSpace(Slice(sep + 1));
  196. if (key == "model name") {
  197. ++num_cpus;
  198. cpu_type = val.ToString();
  199. } else if (key == "cache size") {
  200. cache_size = val.ToString();
  201. }
  202. }
  203. std::fclose(cpuinfo);
  204. std::fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str());
  205. std::fprintf(stderr, "CPUCache: %s\n", cache_size.c_str());
  206. }
  207. #endif
  208. }
  209. void Start() {
  210. start_ = Env::Default()->NowMicros() * 1e-6;
  211. bytes_ = 0;
  212. message_.clear();
  213. last_op_finish_ = start_;
  214. hist_.Clear();
  215. done_ = 0;
  216. next_report_ = 100;
  217. }
  218. void FinishedSingleOp() {
  219. if (FLAGS_histogram) {
  220. double now = Env::Default()->NowMicros() * 1e-6;
  221. double micros = (now - last_op_finish_) * 1e6;
  222. hist_.Add(micros);
  223. if (micros > 20000) {
  224. std::fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
  225. std::fflush(stderr);
  226. }
  227. last_op_finish_ = now;
  228. }
  229. done_++;
  230. if (done_ >= next_report_) {
  231. if (next_report_ < 1000)
  232. next_report_ += 100;
  233. else if (next_report_ < 5000)
  234. next_report_ += 500;
  235. else if (next_report_ < 10000)
  236. next_report_ += 1000;
  237. else if (next_report_ < 50000)
  238. next_report_ += 5000;
  239. else if (next_report_ < 100000)
  240. next_report_ += 10000;
  241. else if (next_report_ < 500000)
  242. next_report_ += 50000;
  243. else
  244. next_report_ += 100000;
  245. std::fprintf(stderr, "... finished %d ops%30s\r", done_, "");
  246. std::fflush(stderr);
  247. }
  248. }
  249. void Stop(const Slice& name) {
  250. double finish = Env::Default()->NowMicros() * 1e-6;
  251. // Pretend at least one op was done in case we are running a benchmark
  252. // that does not call FinishedSingleOp().
  253. if (done_ < 1) done_ = 1;
  254. if (bytes_ > 0) {
  255. char rate[100];
  256. std::snprintf(rate, sizeof(rate), "%6.1f MB/s",
  257. (bytes_ / 1048576.0) / (finish - start_));
  258. if (!message_.empty()) {
  259. message_ = std::string(rate) + " " + message_;
  260. } else {
  261. message_ = rate;
  262. }
  263. }
  264. std::fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
  265. name.ToString().c_str(), (finish - start_) * 1e6 / done_,
  266. (message_.empty() ? "" : " "), message_.c_str());
  267. if (FLAGS_histogram) {
  268. std::fprintf(stdout, "Microseconds per op:\n%s\n",
  269. hist_.ToString().c_str());
  270. }
  271. std::fflush(stdout);
  272. }
  273. public:
  274. enum Order { SEQUENTIAL, RANDOM };
  275. enum DBState { FRESH, EXISTING };
  276. Benchmark()
  277. : db_(nullptr),
  278. db_num_(0),
  279. num_(FLAGS_num),
  280. reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
  281. bytes_(0),
  282. rand_(301) {
  283. std::vector<std::string> files;
  284. std::string test_dir;
  285. Env::Default()->GetTestDirectory(&test_dir);
  286. Env::Default()->GetChildren(test_dir, &files);
  287. if (!FLAGS_use_existing_db) {
  288. for (int i = 0; i < files.size(); i++) {
  289. if (Slice(files[i]).starts_with("dbbench_sqlite3")) {
  290. std::string file_name(test_dir);
  291. file_name += "/";
  292. file_name += files[i];
  293. Env::Default()->RemoveFile(file_name.c_str());
  294. }
  295. }
  296. }
  297. }
  298. ~Benchmark() {
  299. int status = sqlite3_close(db_);
  300. ErrorCheck(status);
  301. }
  302. void Run() {
  303. PrintHeader();
  304. Open();
  305. const char* benchmarks = FLAGS_benchmarks;
  306. while (benchmarks != nullptr) {
  307. const char* sep = strchr(benchmarks, ',');
  308. Slice name;
  309. if (sep == nullptr) {
  310. name = benchmarks;
  311. benchmarks = nullptr;
  312. } else {
  313. name = Slice(benchmarks, sep - benchmarks);
  314. benchmarks = sep + 1;
  315. }
  316. bytes_ = 0;
  317. Start();
  318. bool known = true;
  319. bool write_sync = false;
  320. if (name == Slice("fillseq")) {
  321. Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1);
  322. WalCheckpoint(db_);
  323. } else if (name == Slice("fillseqbatch")) {
  324. Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1000);
  325. WalCheckpoint(db_);
  326. } else if (name == Slice("fillrandom")) {
  327. Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1);
  328. WalCheckpoint(db_);
  329. } else if (name == Slice("fillrandbatch")) {
  330. Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1000);
  331. WalCheckpoint(db_);
  332. } else if (name == Slice("overwrite")) {
  333. Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1);
  334. WalCheckpoint(db_);
  335. } else if (name == Slice("overwritebatch")) {
  336. Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1000);
  337. WalCheckpoint(db_);
  338. } else if (name == Slice("fillrandsync")) {
  339. write_sync = true;
  340. Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1);
  341. WalCheckpoint(db_);
  342. } else if (name == Slice("fillseqsync")) {
  343. write_sync = true;
  344. Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1);
  345. WalCheckpoint(db_);
  346. } else if (name == Slice("fillrand100K")) {
  347. Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1);
  348. WalCheckpoint(db_);
  349. } else if (name == Slice("fillseq100K")) {
  350. Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1);
  351. WalCheckpoint(db_);
  352. } else if (name == Slice("readseq")) {
  353. ReadSequential();
  354. } else if (name == Slice("readrandom")) {
  355. Read(RANDOM, 1);
  356. } else if (name == Slice("readrand100K")) {
  357. int n = reads_;
  358. reads_ /= 1000;
  359. Read(RANDOM, 1);
  360. reads_ = n;
  361. } else {
  362. known = false;
  363. if (name != Slice()) { // No error message for empty name
  364. std::fprintf(stderr, "unknown benchmark '%s'\n",
  365. name.ToString().c_str());
  366. }
  367. }
  368. if (known) {
  369. Stop(name);
  370. }
  371. }
  372. }
  373. void Open() {
  374. assert(db_ == nullptr);
  375. int status;
  376. char file_name[100];
  377. char* err_msg = nullptr;
  378. db_num_++;
  379. // Open database
  380. std::string tmp_dir;
  381. Env::Default()->GetTestDirectory(&tmp_dir);
  382. std::snprintf(file_name, sizeof(file_name), "%s/dbbench_sqlite3-%d.db",
  383. tmp_dir.c_str(), db_num_);
  384. status = sqlite3_open(file_name, &db_);
  385. if (status) {
  386. std::fprintf(stderr, "open error: %s\n", sqlite3_errmsg(db_));
  387. std::exit(1);
  388. }
  389. // Change SQLite cache size
  390. char cache_size[100];
  391. std::snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d",
  392. FLAGS_num_pages);
  393. status = sqlite3_exec(db_, cache_size, nullptr, nullptr, &err_msg);
  394. ExecErrorCheck(status, err_msg);
  395. // FLAGS_page_size is defaulted to 1024
  396. if (FLAGS_page_size != 1024) {
  397. char page_size[100];
  398. std::snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d",
  399. FLAGS_page_size);
  400. status = sqlite3_exec(db_, page_size, nullptr, nullptr, &err_msg);
  401. ExecErrorCheck(status, err_msg);
  402. }
  403. // Change journal mode to WAL if WAL enabled flag is on
  404. if (FLAGS_WAL_enabled) {
  405. std::string WAL_stmt = "PRAGMA journal_mode = WAL";
  406. // LevelDB's default cache size is a combined 4 MB
  407. std::string WAL_checkpoint = "PRAGMA wal_autocheckpoint = 4096";
  408. status = sqlite3_exec(db_, WAL_stmt.c_str(), nullptr, nullptr, &err_msg);
  409. ExecErrorCheck(status, err_msg);
  410. status =
  411. sqlite3_exec(db_, WAL_checkpoint.c_str(), nullptr, nullptr, &err_msg);
  412. ExecErrorCheck(status, err_msg);
  413. }
  414. // Change locking mode to exclusive and create tables/index for database
  415. std::string locking_stmt = "PRAGMA locking_mode = EXCLUSIVE";
  416. std::string create_stmt =
  417. "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))";
  418. if (!FLAGS_use_rowids) create_stmt += " WITHOUT ROWID";
  419. std::string stmt_array[] = {locking_stmt, create_stmt};
  420. int stmt_array_length = sizeof(stmt_array) / sizeof(std::string);
  421. for (int i = 0; i < stmt_array_length; i++) {
  422. status =
  423. sqlite3_exec(db_, stmt_array[i].c_str(), nullptr, nullptr, &err_msg);
  424. ExecErrorCheck(status, err_msg);
  425. }
  426. }
  427. void Write(bool write_sync, Order order, DBState state, int num_entries,
  428. int value_size, int entries_per_batch) {
  429. // Create new database if state == FRESH
  430. if (state == FRESH) {
  431. if (FLAGS_use_existing_db) {
  432. message_ = "skipping (--use_existing_db is true)";
  433. return;
  434. }
  435. sqlite3_close(db_);
  436. db_ = nullptr;
  437. Open();
  438. Start();
  439. }
  440. if (num_entries != num_) {
  441. char msg[100];
  442. std::snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
  443. message_ = msg;
  444. }
  445. char* err_msg = nullptr;
  446. int status;
  447. sqlite3_stmt *replace_stmt, *begin_trans_stmt, *end_trans_stmt;
  448. std::string replace_str = "REPLACE INTO test (key, value) VALUES (?, ?)";
  449. std::string begin_trans_str = "BEGIN TRANSACTION;";
  450. std::string end_trans_str = "END TRANSACTION;";
  451. // Check for synchronous flag in options
  452. std::string sync_stmt =
  453. (write_sync) ? "PRAGMA synchronous = FULL" : "PRAGMA synchronous = OFF";
  454. status = sqlite3_exec(db_, sync_stmt.c_str(), nullptr, nullptr, &err_msg);
  455. ExecErrorCheck(status, err_msg);
  456. // Preparing sqlite3 statements
  457. status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, &replace_stmt,
  458. nullptr);
  459. ErrorCheck(status);
  460. status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1,
  461. &begin_trans_stmt, nullptr);
  462. ErrorCheck(status);
  463. status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, &end_trans_stmt,
  464. nullptr);
  465. ErrorCheck(status);
  466. bool transaction = (entries_per_batch > 1);
  467. for (int i = 0; i < num_entries; i += entries_per_batch) {
  468. // Begin write transaction
  469. if (FLAGS_transaction && transaction) {
  470. status = sqlite3_step(begin_trans_stmt);
  471. StepErrorCheck(status);
  472. status = sqlite3_reset(begin_trans_stmt);
  473. ErrorCheck(status);
  474. }
  475. // Create and execute SQL statements
  476. for (int j = 0; j < entries_per_batch; j++) {
  477. const char* value = gen_.Generate(value_size).data();
  478. // Create values for key-value pair
  479. const int k =
  480. (order == SEQUENTIAL) ? i + j : (rand_.Next() % num_entries);
  481. char key[100];
  482. std::snprintf(key, sizeof(key), "%016d", k);
  483. // Bind KV values into replace_stmt
  484. status = sqlite3_bind_blob(replace_stmt, 1, key, 16, SQLITE_STATIC);
  485. ErrorCheck(status);
  486. status = sqlite3_bind_blob(replace_stmt, 2, value, value_size,
  487. SQLITE_STATIC);
  488. ErrorCheck(status);
  489. // Execute replace_stmt
  490. bytes_ += value_size + strlen(key);
  491. status = sqlite3_step(replace_stmt);
  492. StepErrorCheck(status);
  493. // Reset SQLite statement for another use
  494. status = sqlite3_clear_bindings(replace_stmt);
  495. ErrorCheck(status);
  496. status = sqlite3_reset(replace_stmt);
  497. ErrorCheck(status);
  498. FinishedSingleOp();
  499. }
  500. // End write transaction
  501. if (FLAGS_transaction && transaction) {
  502. status = sqlite3_step(end_trans_stmt);
  503. StepErrorCheck(status);
  504. status = sqlite3_reset(end_trans_stmt);
  505. ErrorCheck(status);
  506. }
  507. }
  508. status = sqlite3_finalize(replace_stmt);
  509. ErrorCheck(status);
  510. status = sqlite3_finalize(begin_trans_stmt);
  511. ErrorCheck(status);
  512. status = sqlite3_finalize(end_trans_stmt);
  513. ErrorCheck(status);
  514. }
  515. void Read(Order order, int entries_per_batch) {
  516. int status;
  517. sqlite3_stmt *read_stmt, *begin_trans_stmt, *end_trans_stmt;
  518. std::string read_str = "SELECT * FROM test WHERE key = ?";
  519. std::string begin_trans_str = "BEGIN TRANSACTION;";
  520. std::string end_trans_str = "END TRANSACTION;";
  521. // Preparing sqlite3 statements
  522. status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1,
  523. &begin_trans_stmt, nullptr);
  524. ErrorCheck(status);
  525. status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, &end_trans_stmt,
  526. nullptr);
  527. ErrorCheck(status);
  528. status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, nullptr);
  529. ErrorCheck(status);
  530. bool transaction = (entries_per_batch > 1);
  531. for (int i = 0; i < reads_; i += entries_per_batch) {
  532. // Begin read transaction
  533. if (FLAGS_transaction && transaction) {
  534. status = sqlite3_step(begin_trans_stmt);
  535. StepErrorCheck(status);
  536. status = sqlite3_reset(begin_trans_stmt);
  537. ErrorCheck(status);
  538. }
  539. // Create and execute SQL statements
  540. for (int j = 0; j < entries_per_batch; j++) {
  541. // Create key value
  542. char key[100];
  543. int k = (order == SEQUENTIAL) ? i + j : (rand_.Next() % reads_);
  544. std::snprintf(key, sizeof(key), "%016d", k);
  545. // Bind key value into read_stmt
  546. status = sqlite3_bind_blob(read_stmt, 1, key, 16, SQLITE_STATIC);
  547. ErrorCheck(status);
  548. // Execute read statement
  549. while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) {
  550. }
  551. StepErrorCheck(status);
  552. // Reset SQLite statement for another use
  553. status = sqlite3_clear_bindings(read_stmt);
  554. ErrorCheck(status);
  555. status = sqlite3_reset(read_stmt);
  556. ErrorCheck(status);
  557. FinishedSingleOp();
  558. }
  559. // End read transaction
  560. if (FLAGS_transaction && transaction) {
  561. status = sqlite3_step(end_trans_stmt);
  562. StepErrorCheck(status);
  563. status = sqlite3_reset(end_trans_stmt);
  564. ErrorCheck(status);
  565. }
  566. }
  567. status = sqlite3_finalize(read_stmt);
  568. ErrorCheck(status);
  569. status = sqlite3_finalize(begin_trans_stmt);
  570. ErrorCheck(status);
  571. status = sqlite3_finalize(end_trans_stmt);
  572. ErrorCheck(status);
  573. }
  574. void ReadSequential() {
  575. int status;
  576. sqlite3_stmt* pStmt;
  577. std::string read_str = "SELECT * FROM test ORDER BY key";
  578. status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, nullptr);
  579. ErrorCheck(status);
  580. for (int i = 0; i < reads_ && SQLITE_ROW == sqlite3_step(pStmt); i++) {
  581. bytes_ += sqlite3_column_bytes(pStmt, 1) + sqlite3_column_bytes(pStmt, 2);
  582. FinishedSingleOp();
  583. }
  584. status = sqlite3_finalize(pStmt);
  585. ErrorCheck(status);
  586. }
  587. };
  588. } // namespace leveldb
  589. int main(int argc, char** argv) {
  590. std::string default_db_path;
  591. for (int i = 1; i < argc; i++) {
  592. double d;
  593. int n;
  594. char junk;
  595. if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
  596. FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
  597. } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
  598. (n == 0 || n == 1)) {
  599. FLAGS_histogram = n;
  600. } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
  601. FLAGS_compression_ratio = d;
  602. } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 &&
  603. (n == 0 || n == 1)) {
  604. FLAGS_use_existing_db = n;
  605. } else if (sscanf(argv[i], "--use_rowids=%d%c", &n, &junk) == 1 &&
  606. (n == 0 || n == 1)) {
  607. FLAGS_use_rowids = n;
  608. } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
  609. FLAGS_num = n;
  610. } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
  611. FLAGS_reads = n;
  612. } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
  613. FLAGS_value_size = n;
  614. } else if (leveldb::Slice(argv[i]) == leveldb::Slice("--no_transaction")) {
  615. FLAGS_transaction = false;
  616. } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) {
  617. FLAGS_page_size = n;
  618. } else if (sscanf(argv[i], "--num_pages=%d%c", &n, &junk) == 1) {
  619. FLAGS_num_pages = n;
  620. } else if (sscanf(argv[i], "--WAL_enabled=%d%c", &n, &junk) == 1 &&
  621. (n == 0 || n == 1)) {
  622. FLAGS_WAL_enabled = n;
  623. } else if (strncmp(argv[i], "--db=", 5) == 0) {
  624. FLAGS_db = argv[i] + 5;
  625. } else {
  626. std::fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
  627. std::exit(1);
  628. }
  629. }
  630. // Choose a location for the test database if none given with --db=<path>
  631. if (FLAGS_db == nullptr) {
  632. leveldb::Env::Default()->GetTestDirectory(&default_db_path);
  633. default_db_path += "/dbbench";
  634. FLAGS_db = default_db_path.c_str();
  635. }
  636. leveldb::Benchmark benchmark;
  637. benchmark.Run();
  638. return 0;
  639. }