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.

400 lines
11 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 <sys/types.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include "db/db_impl.h"
  8. #include "db/version_set.h"
  9. #include "include/cache.h"
  10. #include "include/db.h"
  11. #include "include/env.h"
  12. #include "include/write_batch.h"
  13. #include "util/histogram.h"
  14. #include "util/random.h"
  15. #include "util/testutil.h"
  16. // Comma-separated list of operations to run in the specified order
  17. // Actual benchmarks:
  18. // writeseq -- write N values in sequential key order
  19. // writerandom -- write N values in random key order
  20. // writebig -- write N/1000 100K valuesin random order
  21. // readseq -- read N values sequentially
  22. // readrandom -- read N values in random order
  23. // Meta operations:
  24. // compact -- Compact the entire DB
  25. // heapprofile -- Dump a heap profile (if supported by this port)
  26. // sync -- switch to synchronous writes (not the default)
  27. // nosync -- switch to asynchronous writes (the default)
  28. // tenth -- divide N by 10 (i.e., following benchmarks are smaller)
  29. // normal -- reset N back to its normal value (1000000)
  30. static const char* FLAGS_benchmarks =
  31. "writeseq,"
  32. "writeseq,"
  33. "writerandom,"
  34. "sync,tenth,tenth,writerandom,nosync,normal,"
  35. "readseq,"
  36. "readreverse,"
  37. "readrandom,"
  38. "compact,"
  39. "readseq,"
  40. "readreverse,"
  41. "readrandom,"
  42. "writebig";
  43. // Number of key/values to place in database
  44. static int FLAGS_num = 1000000;
  45. // Size of each value
  46. static int FLAGS_value_size = 100;
  47. // Arrange to generate values that shrink to this fraction of
  48. // their original size after compression
  49. static double FLAGS_compression_ratio = 0.25;
  50. // Print histogram of operation timings
  51. static bool FLAGS_histogram = false;
  52. // Number of bytes to buffer in memtable before compacting
  53. static int FLAGS_write_buffer_size = 1 << 20;
  54. namespace leveldb {
  55. // Helper for quickly generating random data.
  56. namespace {
  57. class RandomGenerator {
  58. private:
  59. std::string data_;
  60. int pos_;
  61. public:
  62. RandomGenerator() {
  63. // We use a limited amount of data over and over again and ensure
  64. // that it is larger than the compression window (32KB), and also
  65. // large enough to serve all typical value sizes we want to write.
  66. Random rnd(301);
  67. std::string piece;
  68. while (data_.size() < 1048576) {
  69. // Add a short fragment that is as compressible as specified
  70. // by FLAGS_compression_ratio.
  71. test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
  72. data_.append(piece);
  73. }
  74. pos_ = 0;
  75. }
  76. Slice Generate(int len) {
  77. if (pos_ + len > data_.size()) {
  78. pos_ = 0;
  79. assert(len < data_.size());
  80. }
  81. pos_ += len;
  82. return Slice(data_.data() + pos_ - len, len);
  83. }
  84. };
  85. }
  86. class Benchmark {
  87. private:
  88. Cache* cache_;
  89. DB* db_;
  90. int num_;
  91. bool sync_;
  92. int heap_counter_;
  93. double start_;
  94. double last_op_finish_;
  95. int64_t bytes_;
  96. std::string message_;
  97. Histogram hist_;
  98. RandomGenerator gen_;
  99. Random rand_;
  100. // State kept for progress messages
  101. int done_;
  102. int next_report_; // When to report next
  103. void Start() {
  104. start_ = Env::Default()->NowMicros() * 1e-6;
  105. bytes_ = 0;
  106. message_.clear();
  107. last_op_finish_ = start_;
  108. hist_.Clear();
  109. done_ = 0;
  110. next_report_ = 100;
  111. }
  112. void FinishedSingleOp() {
  113. if (FLAGS_histogram) {
  114. double now = Env::Default()->NowMicros() * 1e-6;
  115. double micros = (now - last_op_finish_) * 1e6;
  116. hist_.Add(micros);
  117. if (micros > 20000) {
  118. fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
  119. fflush(stderr);
  120. }
  121. last_op_finish_ = now;
  122. }
  123. done_++;
  124. if (done_ >= next_report_) {
  125. if (next_report_ < 1000) {
  126. next_report_ += 100;
  127. } else if (next_report_ < 10000) {
  128. next_report_ += 1000;
  129. } else if (next_report_ < 100000) {
  130. next_report_ += 10000;
  131. } else {
  132. next_report_ += 100000;
  133. }
  134. fprintf(stderr, "... finished %d ops%30s\r", done_, "");
  135. fflush(stderr);
  136. }
  137. }
  138. void Stop(const Slice& name) {
  139. double finish = Env::Default()->NowMicros() * 1e-6;
  140. // Pretend at least one op was done in case we are running a benchmark
  141. // that does nto call FinishedSingleOp().
  142. if (done_ < 1) done_ = 1;
  143. if (bytes_ > 0) {
  144. char rate[100];
  145. snprintf(rate, sizeof(rate), "%5.1f MB/s",
  146. (bytes_ / 1048576.0) / (finish - start_));
  147. if (!message_.empty()) {
  148. message_.push_back(' ');
  149. }
  150. message_.append(rate);
  151. }
  152. fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
  153. name.ToString().c_str(),
  154. (finish - start_) * 1e6 / done_,
  155. (message_.empty() ? "" : " "),
  156. message_.c_str());
  157. if (FLAGS_histogram) {
  158. fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
  159. }
  160. fflush(stdout);
  161. }
  162. public:
  163. enum Order {
  164. SEQUENTIAL,
  165. REVERSE, // Currently only supported for reads
  166. RANDOM
  167. };
  168. Benchmark() : cache_(NewLRUCache(200<<20)),
  169. db_(NULL),
  170. num_(FLAGS_num),
  171. sync_(false),
  172. heap_counter_(0),
  173. bytes_(0),
  174. rand_(301) {
  175. std::vector<std::string> files;
  176. Env::Default()->GetChildren("/tmp/dbbench", &files);
  177. for (int i = 0; i < files.size(); i++) {
  178. if (Slice(files[i]).starts_with("heap-")) {
  179. Env::Default()->DeleteFile("/tmp/dbbench/" + files[i]);
  180. }
  181. }
  182. DestroyDB("/tmp/dbbench", Options());
  183. }
  184. ~Benchmark() {
  185. delete db_;
  186. delete cache_;
  187. }
  188. void Run() {
  189. Options options;
  190. options.create_if_missing = true;
  191. options.max_open_files = 10000;
  192. options.block_cache = cache_;
  193. options.write_buffer_size = FLAGS_write_buffer_size;
  194. Start();
  195. Status s = DB::Open(options, "/tmp/dbbench", &db_);
  196. Stop("open");
  197. if (!s.ok()) {
  198. fprintf(stderr, "open error: %s\n", s.ToString().c_str());
  199. exit(1);
  200. }
  201. const char* benchmarks = FLAGS_benchmarks;
  202. while (benchmarks != NULL) {
  203. const char* sep = strchr(benchmarks, ',');
  204. Slice name;
  205. if (sep == NULL) {
  206. name = benchmarks;
  207. benchmarks = NULL;
  208. } else {
  209. name = Slice(benchmarks, sep - benchmarks);
  210. benchmarks = sep + 1;
  211. }
  212. Start();
  213. if (name == Slice("writeseq")) {
  214. Write(SEQUENTIAL, num_, FLAGS_value_size);
  215. } else if (name == Slice("writerandom")) {
  216. Write(RANDOM, num_, FLAGS_value_size);
  217. } else if (name == Slice("writebig")) {
  218. Write(RANDOM, num_ / 1000, 100 * 1000);
  219. } else if (name == Slice("readseq")) {
  220. Read(SEQUENTIAL);
  221. } else if (name == Slice("readreverse")) {
  222. Read(REVERSE);
  223. } else if (name == Slice("readrandom")) {
  224. Read(RANDOM);
  225. } else if (name == Slice("compact")) {
  226. Compact();
  227. } else if (name == Slice("heapprofile")) {
  228. HeapProfile();
  229. } else if (name == Slice("sync")) {
  230. sync_ = true;
  231. } else if (name == Slice("nosync")) {
  232. sync_ = false;
  233. } else if (name == Slice("tenth")) {
  234. num_ = num_ / 10;
  235. } else if (name == Slice("normal")) {
  236. num_ = FLAGS_num;
  237. } else {
  238. fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
  239. }
  240. Stop(name);
  241. }
  242. }
  243. void Write(Order order, int num_entries, int value_size) {
  244. WriteBatch batch;
  245. Status s;
  246. std::string val;
  247. WriteOptions options;
  248. options.sync = sync_;
  249. for (int i = 0; i < num_entries; i++) {
  250. const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % FLAGS_num);
  251. char key[100];
  252. snprintf(key, sizeof(key), "%012d", k);
  253. batch.Clear();
  254. batch.Put(key, gen_.Generate(value_size));
  255. s = db_->Write(options, &batch);
  256. bytes_ += value_size + strlen(key);
  257. if (!s.ok()) {
  258. fprintf(stderr, "put error: %s\n", s.ToString().c_str());
  259. exit(1);
  260. }
  261. FinishedSingleOp();
  262. }
  263. }
  264. void Read(Order order) {
  265. ReadOptions options;
  266. switch (order) {
  267. case SEQUENTIAL: {
  268. Iterator* iter = db_->NewIterator(options);
  269. int i = 0;
  270. for (iter->SeekToFirst(); i < num_ && iter->Valid(); iter->Next()) {
  271. bytes_ += iter->key().size() + iter->value().size();
  272. FinishedSingleOp();
  273. ++i;
  274. }
  275. delete iter;
  276. break;
  277. }
  278. case REVERSE: {
  279. Iterator* iter = db_->NewIterator(options);
  280. int i = 0;
  281. for (iter->SeekToLast(); i < num_ && iter->Valid(); iter->Prev()) {
  282. bytes_ += iter->key().size() + iter->value().size();
  283. FinishedSingleOp();
  284. ++i;
  285. }
  286. delete iter;
  287. break;
  288. }
  289. case RANDOM: {
  290. std::string value;
  291. for (int i = 0; i < num_; i++) {
  292. char key[100];
  293. const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % FLAGS_num);
  294. snprintf(key, sizeof(key), "%012d", k);
  295. db_->Get(options, key, &value);
  296. FinishedSingleOp();
  297. }
  298. break;
  299. }
  300. }
  301. }
  302. void Compact() {
  303. DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
  304. dbi->TEST_CompactMemTable();
  305. int max_level_with_files = 1;
  306. for (int level = 1; level < config::kNumLevels; level++) {
  307. uint64_t v;
  308. char name[100];
  309. snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level);
  310. if (db_->GetProperty(name, &v) && v > 0) {
  311. max_level_with_files = level;
  312. }
  313. }
  314. for (int level = 0; level < max_level_with_files; level++) {
  315. dbi->TEST_CompactRange(level, "", "~");
  316. }
  317. }
  318. static void WriteToFile(void* arg, const char* buf, int n) {
  319. reinterpret_cast<WritableFile*>(arg)->Append(Slice(buf, n));
  320. }
  321. void HeapProfile() {
  322. char fname[100];
  323. snprintf(fname, sizeof(fname), "/tmp/dbbench/heap-%04d", ++heap_counter_);
  324. WritableFile* file;
  325. Status s = Env::Default()->NewWritableFile(fname, &file);
  326. if (!s.ok()) {
  327. message_ = s.ToString();
  328. return;
  329. }
  330. bool ok = port::GetHeapProfile(WriteToFile, file);
  331. delete file;
  332. if (!ok) {
  333. message_ = "not supported";
  334. Env::Default()->DeleteFile(fname);
  335. }
  336. }
  337. };
  338. }
  339. int main(int argc, char** argv) {
  340. for (int i = 1; i < argc; i++) {
  341. double d;
  342. int n;
  343. char junk;
  344. if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
  345. FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
  346. } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
  347. FLAGS_compression_ratio = d;
  348. } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
  349. (n == 0 || n == 1)) {
  350. FLAGS_histogram = n;
  351. } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
  352. FLAGS_num = n;
  353. } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
  354. FLAGS_value_size = n;
  355. } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) {
  356. FLAGS_write_buffer_size = n;
  357. } else {
  358. fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
  359. exit(1);
  360. }
  361. }
  362. leveldb::Benchmark benchmark;
  363. benchmark.Run();
  364. return 0;
  365. }