作者: 谢瑞阳 10225101483 徐翔宇 10225101535
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.

528 lines
16 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 <stdio.h>
  5. #include <stdlib.h>
  6. #include <kcpolydb.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. // fillrandom -- write N values in random key order in async mode
  15. // overwrite -- overwrite N values in random key order in async mode
  16. // fillseqsync -- write N/100 values in sequential key order in sync mode
  17. // fillrandsync -- write N/100 values in random key order in sync mode
  18. // fillrand100K -- write N/1000 100K values in random order in async mode
  19. // fillseq100K -- write N/1000 100K values in seq order in async mode
  20. // readseq -- read N times sequentially
  21. // readseq100K -- read N/1000 100K values in sequential order in async mode
  22. // readrand100K -- read N/1000 100K values in sequential order in async mode
  23. // readrandom -- read N times in random order
  24. static const char* FLAGS_benchmarks =
  25. "fillseq,"
  26. "fillseqsync,"
  27. "fillrandsync,"
  28. "fillrandom,"
  29. "overwrite,"
  30. "readrandom,"
  31. "readseq,"
  32. "fillrand100K,"
  33. "fillseq100K,"
  34. "readseq100K,"
  35. "readrand100K,"
  36. ;
  37. // Number of key/values to place in database
  38. static int FLAGS_num = 1000000;
  39. // Number of read operations to do. If negative, do FLAGS_num reads.
  40. static int FLAGS_reads = -1;
  41. // Size of each value
  42. static int FLAGS_value_size = 100;
  43. // Arrange to generate values that shrink to this fraction of
  44. // their original size after compression
  45. static double FLAGS_compression_ratio = 0.5;
  46. // Print histogram of operation timings
  47. static bool FLAGS_histogram = false;
  48. // Cache size. Default 4 MB
  49. static int FLAGS_cache_size = 4194304;
  50. // Page size. Default 1 KB
  51. static int FLAGS_page_size = 1024;
  52. // If true, do not destroy the existing database. If you set this
  53. // flag and also specify a benchmark that wants a fresh database, that
  54. // benchmark will fail.
  55. static bool FLAGS_use_existing_db = false;
  56. // Compression flag. If true, compression is on. If false, compression
  57. // is off.
  58. static bool FLAGS_compression = true;
  59. // Use the db with the following name.
  60. static const char* FLAGS_db = NULL;
  61. inline
  62. static void DBSynchronize(kyotocabinet::TreeDB* db_)
  63. {
  64. // Synchronize will flush writes to disk
  65. if (!db_->synchronize()) {
  66. fprintf(stderr, "synchronize error: %s\n", db_->error().name());
  67. }
  68. }
  69. namespace leveldb {
  70. // Helper for quickly generating random data.
  71. namespace {
  72. class RandomGenerator {
  73. private:
  74. std::string data_;
  75. int pos_;
  76. public:
  77. RandomGenerator() {
  78. // We use a limited amount of data over and over again and ensure
  79. // that it is larger than the compression window (32KB), and also
  80. // large enough to serve all typical value sizes we want to write.
  81. Random rnd(301);
  82. std::string piece;
  83. while (data_.size() < 1048576) {
  84. // Add a short fragment that is as compressible as specified
  85. // by FLAGS_compression_ratio.
  86. test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
  87. data_.append(piece);
  88. }
  89. pos_ = 0;
  90. }
  91. Slice Generate(int len) {
  92. if (pos_ + len > data_.size()) {
  93. pos_ = 0;
  94. assert(len < data_.size());
  95. }
  96. pos_ += len;
  97. return Slice(data_.data() + pos_ - len, len);
  98. }
  99. };
  100. static Slice TrimSpace(Slice s) {
  101. int start = 0;
  102. while (start < s.size() && isspace(s[start])) {
  103. start++;
  104. }
  105. int limit = s.size();
  106. while (limit > start && isspace(s[limit-1])) {
  107. limit--;
  108. }
  109. return Slice(s.data() + start, limit - start);
  110. }
  111. } // namespace
  112. class Benchmark {
  113. private:
  114. kyotocabinet::TreeDB* db_;
  115. int db_num_;
  116. int num_;
  117. int reads_;
  118. double start_;
  119. double last_op_finish_;
  120. int64_t bytes_;
  121. std::string message_;
  122. Histogram hist_;
  123. RandomGenerator gen_;
  124. Random rand_;
  125. kyotocabinet::LZOCompressor<kyotocabinet::LZO::RAW> comp_;
  126. // State kept for progress messages
  127. int done_;
  128. int next_report_; // When to report next
  129. void PrintHeader() {
  130. const int kKeySize = 16;
  131. PrintEnvironment();
  132. fprintf(stdout, "Keys: %d bytes each\n", kKeySize);
  133. fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n",
  134. FLAGS_value_size,
  135. static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));
  136. fprintf(stdout, "Entries: %d\n", num_);
  137. fprintf(stdout, "RawSize: %.1f MB (estimated)\n",
  138. ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_)
  139. / 1048576.0));
  140. fprintf(stdout, "FileSize: %.1f MB (estimated)\n",
  141. (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_)
  142. / 1048576.0));
  143. PrintWarnings();
  144. fprintf(stdout, "------------------------------------------------\n");
  145. }
  146. void PrintWarnings() {
  147. #if defined(__GNUC__) && !defined(__OPTIMIZE__)
  148. fprintf(stdout,
  149. "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"
  150. );
  151. #endif
  152. #ifndef NDEBUG
  153. fprintf(stdout,
  154. "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
  155. #endif
  156. }
  157. void PrintEnvironment() {
  158. fprintf(stderr, "Kyoto Cabinet: version %s, lib ver %d, lib rev %d\n",
  159. kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV);
  160. #if defined(__linux)
  161. time_t now = time(NULL);
  162. fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline
  163. FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
  164. if (cpuinfo != NULL) {
  165. char line[1000];
  166. int num_cpus = 0;
  167. std::string cpu_type;
  168. std::string cache_size;
  169. while (fgets(line, sizeof(line), cpuinfo) != NULL) {
  170. const char* sep = strchr(line, ':');
  171. if (sep == NULL) {
  172. continue;
  173. }
  174. Slice key = TrimSpace(Slice(line, sep - 1 - line));
  175. Slice val = TrimSpace(Slice(sep + 1));
  176. if (key == "model name") {
  177. ++num_cpus;
  178. cpu_type = val.ToString();
  179. } else if (key == "cache size") {
  180. cache_size = val.ToString();
  181. }
  182. }
  183. fclose(cpuinfo);
  184. fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str());
  185. fprintf(stderr, "CPUCache: %s\n", cache_size.c_str());
  186. }
  187. #endif
  188. }
  189. void Start() {
  190. start_ = Env::Default()->NowMicros() * 1e-6;
  191. bytes_ = 0;
  192. message_.clear();
  193. last_op_finish_ = start_;
  194. hist_.Clear();
  195. done_ = 0;
  196. next_report_ = 100;
  197. }
  198. void FinishedSingleOp() {
  199. if (FLAGS_histogram) {
  200. double now = Env::Default()->NowMicros() * 1e-6;
  201. double micros = (now - last_op_finish_) * 1e6;
  202. hist_.Add(micros);
  203. if (micros > 20000) {
  204. fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
  205. fflush(stderr);
  206. }
  207. last_op_finish_ = now;
  208. }
  209. done_++;
  210. if (done_ >= next_report_) {
  211. if (next_report_ < 1000) next_report_ += 100;
  212. else if (next_report_ < 5000) next_report_ += 500;
  213. else if (next_report_ < 10000) next_report_ += 1000;
  214. else if (next_report_ < 50000) next_report_ += 5000;
  215. else if (next_report_ < 100000) next_report_ += 10000;
  216. else if (next_report_ < 500000) next_report_ += 50000;
  217. else next_report_ += 100000;
  218. fprintf(stderr, "... finished %d ops%30s\r", done_, "");
  219. fflush(stderr);
  220. }
  221. }
  222. void Stop(const Slice& name) {
  223. double finish = Env::Default()->NowMicros() * 1e-6;
  224. // Pretend at least one op was done in case we are running a benchmark
  225. // that does not call FinishedSingleOp().
  226. if (done_ < 1) done_ = 1;
  227. if (bytes_ > 0) {
  228. char rate[100];
  229. snprintf(rate, sizeof(rate), "%6.1f MB/s",
  230. (bytes_ / 1048576.0) / (finish - start_));
  231. if (!message_.empty()) {
  232. message_ = std::string(rate) + " " + message_;
  233. } else {
  234. message_ = rate;
  235. }
  236. }
  237. fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
  238. name.ToString().c_str(),
  239. (finish - start_) * 1e6 / done_,
  240. (message_.empty() ? "" : " "),
  241. message_.c_str());
  242. if (FLAGS_histogram) {
  243. fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
  244. }
  245. fflush(stdout);
  246. }
  247. public:
  248. enum Order {
  249. SEQUENTIAL,
  250. RANDOM
  251. };
  252. enum DBState {
  253. FRESH,
  254. EXISTING
  255. };
  256. Benchmark()
  257. : db_(NULL),
  258. num_(FLAGS_num),
  259. reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
  260. bytes_(0),
  261. rand_(301) {
  262. std::vector<std::string> files;
  263. std::string test_dir;
  264. Env::Default()->GetTestDirectory(&test_dir);
  265. Env::Default()->GetChildren(test_dir.c_str(), &files);
  266. if (!FLAGS_use_existing_db) {
  267. for (int i = 0; i < files.size(); i++) {
  268. if (Slice(files[i]).starts_with("dbbench_polyDB")) {
  269. std::string file_name(test_dir);
  270. file_name += "/";
  271. file_name += files[i];
  272. Env::Default()->DeleteFile(file_name.c_str());
  273. }
  274. }
  275. }
  276. }
  277. ~Benchmark() {
  278. if (!db_->close()) {
  279. fprintf(stderr, "close error: %s\n", db_->error().name());
  280. }
  281. }
  282. void Run() {
  283. PrintHeader();
  284. Open(false);
  285. const char* benchmarks = FLAGS_benchmarks;
  286. while (benchmarks != NULL) {
  287. const char* sep = strchr(benchmarks, ',');
  288. Slice name;
  289. if (sep == NULL) {
  290. name = benchmarks;
  291. benchmarks = NULL;
  292. } else {
  293. name = Slice(benchmarks, sep - benchmarks);
  294. benchmarks = sep + 1;
  295. }
  296. Start();
  297. bool known = true;
  298. bool write_sync = false;
  299. if (name == Slice("fillseq")) {
  300. Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1);
  301. } else if (name == Slice("fillrandom")) {
  302. Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1);
  303. DBSynchronize(db_);
  304. } else if (name == Slice("overwrite")) {
  305. Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1);
  306. DBSynchronize(db_);
  307. } else if (name == Slice("fillrandsync")) {
  308. write_sync = true;
  309. Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1);
  310. DBSynchronize(db_);
  311. } else if (name == Slice("fillseqsync")) {
  312. write_sync = true;
  313. Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1);
  314. DBSynchronize(db_);
  315. } else if (name == Slice("fillrand100K")) {
  316. Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1);
  317. DBSynchronize(db_);
  318. } else if (name == Slice("fillseq100K")) {
  319. Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1);
  320. DBSynchronize(db_);
  321. } else if (name == Slice("readseq")) {
  322. ReadSequential();
  323. } else if (name == Slice("readrandom")) {
  324. ReadRandom();
  325. } else if (name == Slice("readrand100K")) {
  326. int n = reads_;
  327. reads_ /= 1000;
  328. ReadRandom();
  329. reads_ = n;
  330. } else if (name == Slice("readseq100K")) {
  331. int n = reads_;
  332. reads_ /= 1000;
  333. ReadSequential();
  334. reads_ = n;
  335. } else {
  336. known = false;
  337. if (name != Slice()) { // No error message for empty name
  338. fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
  339. }
  340. }
  341. if (known) {
  342. Stop(name);
  343. }
  344. }
  345. }
  346. private:
  347. void Open(bool sync) {
  348. assert(db_ == NULL);
  349. // Initialize db_
  350. db_ = new kyotocabinet::TreeDB();
  351. char file_name[100];
  352. db_num_++;
  353. std::string test_dir;
  354. Env::Default()->GetTestDirectory(&test_dir);
  355. snprintf(file_name, sizeof(file_name),
  356. "%s/dbbench_polyDB-%d.kct",
  357. test_dir.c_str(),
  358. db_num_);
  359. // Create tuning options and open the database
  360. int open_options = kyotocabinet::PolyDB::OWRITER |
  361. kyotocabinet::PolyDB::OCREATE;
  362. int tune_options = kyotocabinet::TreeDB::TSMALL |
  363. kyotocabinet::TreeDB::TLINEAR;
  364. if (FLAGS_compression) {
  365. tune_options |= kyotocabinet::TreeDB::TCOMPRESS;
  366. db_->tune_compressor(&comp_);
  367. }
  368. db_->tune_options(tune_options);
  369. db_->tune_page_cache(FLAGS_cache_size);
  370. db_->tune_page(FLAGS_page_size);
  371. db_->tune_map(256LL<<20);
  372. if (sync) {
  373. open_options |= kyotocabinet::PolyDB::OAUTOSYNC;
  374. }
  375. if (!db_->open(file_name, open_options)) {
  376. fprintf(stderr, "open error: %s\n", db_->error().name());
  377. }
  378. }
  379. void Write(bool sync, Order order, DBState state,
  380. int num_entries, int value_size, int entries_per_batch) {
  381. // Create new database if state == FRESH
  382. if (state == FRESH) {
  383. if (FLAGS_use_existing_db) {
  384. message_ = "skipping (--use_existing_db is true)";
  385. return;
  386. }
  387. delete db_;
  388. db_ = NULL;
  389. Open(sync);
  390. Start(); // Do not count time taken to destroy/open
  391. }
  392. if (num_entries != num_) {
  393. char msg[100];
  394. snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
  395. message_ = msg;
  396. }
  397. // Write to database
  398. for (int i = 0; i < num_entries; i++)
  399. {
  400. const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % num_entries);
  401. char key[100];
  402. snprintf(key, sizeof(key), "%016d", k);
  403. bytes_ += value_size + strlen(key);
  404. std::string cpp_key = key;
  405. if (!db_->set(cpp_key, gen_.Generate(value_size).ToString())) {
  406. fprintf(stderr, "set error: %s\n", db_->error().name());
  407. }
  408. FinishedSingleOp();
  409. }
  410. }
  411. void ReadSequential() {
  412. kyotocabinet::DB::Cursor* cur = db_->cursor();
  413. cur->jump();
  414. std::string ckey, cvalue;
  415. while (cur->get(&ckey, &cvalue, true)) {
  416. bytes_ += ckey.size() + cvalue.size();
  417. FinishedSingleOp();
  418. }
  419. delete cur;
  420. }
  421. void ReadRandom() {
  422. std::string value;
  423. for (int i = 0; i < reads_; i++) {
  424. char key[100];
  425. const int k = rand_.Next() % reads_;
  426. snprintf(key, sizeof(key), "%016d", k);
  427. db_->get(key, &value);
  428. FinishedSingleOp();
  429. }
  430. }
  431. };
  432. } // namespace leveldb
  433. int main(int argc, char** argv) {
  434. std::string default_db_path;
  435. for (int i = 1; i < argc; i++) {
  436. double d;
  437. int n;
  438. char junk;
  439. if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
  440. FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
  441. } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
  442. FLAGS_compression_ratio = d;
  443. } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
  444. (n == 0 || n == 1)) {
  445. FLAGS_histogram = n;
  446. } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
  447. FLAGS_num = n;
  448. } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
  449. FLAGS_reads = n;
  450. } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
  451. FLAGS_value_size = n;
  452. } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) {
  453. FLAGS_cache_size = n;
  454. } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) {
  455. FLAGS_page_size = n;
  456. } else if (sscanf(argv[i], "--compression=%d%c", &n, &junk) == 1 &&
  457. (n == 0 || n == 1)) {
  458. FLAGS_compression = (n == 1) ? true : false;
  459. } else if (strncmp(argv[i], "--db=", 5) == 0) {
  460. FLAGS_db = argv[i] + 5;
  461. } else {
  462. fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
  463. exit(1);
  464. }
  465. }
  466. // Choose a location for the test database if none given with --db=<path>
  467. if (FLAGS_db == NULL) {
  468. leveldb::Env::Default()->GetTestDirectory(&default_db_path);
  469. default_db_path += "/dbbench";
  470. FLAGS_db = default_db_path.c_str();
  471. }
  472. leveldb::Benchmark benchmark;
  473. benchmark.Run();
  474. return 0;
  475. }