小组成员:姚凯文(kevinyao0901),姜嘉琪
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.

1125 lines
34 KiB

Add support for Zstd-based compression in LevelDB. This change implements support for Zstd-based compression in LevelDB. Building up from the Snappy compression (which has been supported since inception), this change adds Zstd as an alternate compression algorithm. We are implementing this to provide alternative options for users who might have different performance and efficiency requirements. For instance, the Zstandard website (https://facebook.github.io/zstd/) claims that the Zstd algorithm can achieve around 30% higher compression ratios than Snappy, with relatively smaller (~10%) slowdowns in de/compression speeds. Benchmarking results: $ blaze-bin/third_party/leveldb/db_bench LevelDB: version 1.23 Date: Thu Feb 2 18:50:06 2023 CPU: 56 * Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz CPUCache: 35840 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) ------------------------------------------------ fillseq : 2.613 micros/op; 42.3 MB/s fillsync : 3924.432 micros/op; 0.0 MB/s (1000 ops) fillrandom : 3.609 micros/op; 30.7 MB/s overwrite : 4.508 micros/op; 24.5 MB/s readrandom : 6.136 micros/op; (864322 of 1000000 found) readrandom : 5.446 micros/op; (864083 of 1000000 found) readseq : 0.180 micros/op; 613.3 MB/s readreverse : 0.321 micros/op; 344.7 MB/s compact : 827043.000 micros/op; readrandom : 4.603 micros/op; (864105 of 1000000 found) readseq : 0.169 micros/op; 656.3 MB/s readreverse : 0.315 micros/op; 350.8 MB/s fill100K : 854.009 micros/op; 111.7 MB/s (1000 ops) crc32c : 1.227 micros/op; 3184.0 MB/s (4K per op) snappycomp : 3.610 micros/op; 1081.9 MB/s (output: 55.2%) snappyuncomp : 0.691 micros/op; 5656.3 MB/s zstdcomp : 15.731 micros/op; 248.3 MB/s (output: 44.1%) zstduncomp : 4.218 micros/op; 926.2 MB/s PiperOrigin-RevId: 509957778
1 year ago
Add support for Zstd-based compression in LevelDB. This change implements support for Zstd-based compression in LevelDB. Building up from the Snappy compression (which has been supported since inception), this change adds Zstd as an alternate compression algorithm. We are implementing this to provide alternative options for users who might have different performance and efficiency requirements. For instance, the Zstandard website (https://facebook.github.io/zstd/) claims that the Zstd algorithm can achieve around 30% higher compression ratios than Snappy, with relatively smaller (~10%) slowdowns in de/compression speeds. Benchmarking results: $ blaze-bin/third_party/leveldb/db_bench LevelDB: version 1.23 Date: Thu Feb 2 18:50:06 2023 CPU: 56 * Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz CPUCache: 35840 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) ------------------------------------------------ fillseq : 2.613 micros/op; 42.3 MB/s fillsync : 3924.432 micros/op; 0.0 MB/s (1000 ops) fillrandom : 3.609 micros/op; 30.7 MB/s overwrite : 4.508 micros/op; 24.5 MB/s readrandom : 6.136 micros/op; (864322 of 1000000 found) readrandom : 5.446 micros/op; (864083 of 1000000 found) readseq : 0.180 micros/op; 613.3 MB/s readreverse : 0.321 micros/op; 344.7 MB/s compact : 827043.000 micros/op; readrandom : 4.603 micros/op; (864105 of 1000000 found) readseq : 0.169 micros/op; 656.3 MB/s readreverse : 0.315 micros/op; 350.8 MB/s fill100K : 854.009 micros/op; 111.7 MB/s (1000 ops) crc32c : 1.227 micros/op; 3184.0 MB/s (4K per op) snappycomp : 3.610 micros/op; 1081.9 MB/s (output: 55.2%) snappyuncomp : 0.691 micros/op; 5656.3 MB/s zstdcomp : 15.731 micros/op; 248.3 MB/s (output: 44.1%) zstduncomp : 4.218 micros/op; 926.2 MB/s PiperOrigin-RevId: 509957778
1 year 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
4 years ago
Release 1.18 Changes are: * Update version number to 1.18 * Replace the basic fprintf call with a call to fwrite in order to work around the apparent compiler optimization/rewrite failure that we are seeing with the new toolchain/iOS SDKs provided with Xcode6 and iOS8. * Fix ALL the header guards. * Createed a README.md with the LevelDB project description. * A new CONTRIBUTING file. * Don't implicitly convert uint64_t to size_t or int. Either preserve it as uint64_t, or explicitly cast. This fixes MSVC warnings about possible value truncation when compiling this code in Chromium. * Added a DumpFile() library function that encapsulates the guts of the "leveldbutil dump" command. This will allow clients to dump data to their log files instead of stdout. It will also allow clients to supply their own environment. * leveldb: Remove unused function 'ConsumeChar'. * leveldbutil: Remove unused member variables from WriteBatchItemPrinter. * OpenBSD, NetBSD and DragonflyBSD have _LITTLE_ENDIAN, so define PLATFORM_IS_LITTLE_ENDIAN like on FreeBSD. This fixes: * issue #143 * issue #198 * issue #249 * Switch from <cstdatomic> to <atomic>. The former never made it into the standard and doesn't exist in modern gcc versions at all. The later contains everything that leveldb was using from the former. This problem was noticed when porting to Portable Native Client where no memory barrier is defined. The fact that <cstdatomic> is missing normally goes unnoticed since memory barriers are defined for most architectures. * Make Hash() treat its input as unsigned. Before this change LevelDB files from platforms with different signedness of char were not compatible. This change fixes: issue #243 * Verify checksums of index/meta/filter blocks when paranoid_checks set. * Invoke all tools for iOS with xcrun. (This was causing problems with the new XCode 5.1.1 image on pulse.) * include <sys/stat.h> only once, and fix the following linter warning: "Found C system header after C++ system header" * When encountering a corrupted table file, return Status::Corruption instead of Status::InvalidArgument. * Support cygwin as build platform, patch is from https://code.google.com/p/leveldb/issues/detail?id=188 * Fix typo, merge patch from https://code.google.com/p/leveldb/issues/detail?id=159 * Fix typos and comments, and address the following two issues: * issue #166 * issue #241 * Add missing db synchronize after "fillseq" in the benchmark. * Removed unused variable in SeekRandom: value (issue #201)
10 years ago
Add support for Zstd-based compression in LevelDB. This change implements support for Zstd-based compression in LevelDB. Building up from the Snappy compression (which has been supported since inception), this change adds Zstd as an alternate compression algorithm. We are implementing this to provide alternative options for users who might have different performance and efficiency requirements. For instance, the Zstandard website (https://facebook.github.io/zstd/) claims that the Zstd algorithm can achieve around 30% higher compression ratios than Snappy, with relatively smaller (~10%) slowdowns in de/compression speeds. Benchmarking results: $ blaze-bin/third_party/leveldb/db_bench LevelDB: version 1.23 Date: Thu Feb 2 18:50:06 2023 CPU: 56 * Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz CPUCache: 35840 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) ------------------------------------------------ fillseq : 2.613 micros/op; 42.3 MB/s fillsync : 3924.432 micros/op; 0.0 MB/s (1000 ops) fillrandom : 3.609 micros/op; 30.7 MB/s overwrite : 4.508 micros/op; 24.5 MB/s readrandom : 6.136 micros/op; (864322 of 1000000 found) readrandom : 5.446 micros/op; (864083 of 1000000 found) readseq : 0.180 micros/op; 613.3 MB/s readreverse : 0.321 micros/op; 344.7 MB/s compact : 827043.000 micros/op; readrandom : 4.603 micros/op; (864105 of 1000000 found) readseq : 0.169 micros/op; 656.3 MB/s readreverse : 0.315 micros/op; 350.8 MB/s fill100K : 854.009 micros/op; 111.7 MB/s (1000 ops) crc32c : 1.227 micros/op; 3184.0 MB/s (4K per op) snappycomp : 3.610 micros/op; 1081.9 MB/s (output: 55.2%) snappyuncomp : 0.691 micros/op; 5656.3 MB/s zstdcomp : 15.731 micros/op; 248.3 MB/s (output: 44.1%) zstduncomp : 4.218 micros/op; 926.2 MB/s PiperOrigin-RevId: 509957778
1 year ago
Add support for Zstd-based compression in LevelDB. This change implements support for Zstd-based compression in LevelDB. Building up from the Snappy compression (which has been supported since inception), this change adds Zstd as an alternate compression algorithm. We are implementing this to provide alternative options for users who might have different performance and efficiency requirements. For instance, the Zstandard website (https://facebook.github.io/zstd/) claims that the Zstd algorithm can achieve around 30% higher compression ratios than Snappy, with relatively smaller (~10%) slowdowns in de/compression speeds. Benchmarking results: $ blaze-bin/third_party/leveldb/db_bench LevelDB: version 1.23 Date: Thu Feb 2 18:50:06 2023 CPU: 56 * Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz CPUCache: 35840 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) ------------------------------------------------ fillseq : 2.613 micros/op; 42.3 MB/s fillsync : 3924.432 micros/op; 0.0 MB/s (1000 ops) fillrandom : 3.609 micros/op; 30.7 MB/s overwrite : 4.508 micros/op; 24.5 MB/s readrandom : 6.136 micros/op; (864322 of 1000000 found) readrandom : 5.446 micros/op; (864083 of 1000000 found) readseq : 0.180 micros/op; 613.3 MB/s readreverse : 0.321 micros/op; 344.7 MB/s compact : 827043.000 micros/op; readrandom : 4.603 micros/op; (864105 of 1000000 found) readseq : 0.169 micros/op; 656.3 MB/s readreverse : 0.315 micros/op; 350.8 MB/s fill100K : 854.009 micros/op; 111.7 MB/s (1000 ops) crc32c : 1.227 micros/op; 3184.0 MB/s (4K per op) snappycomp : 3.610 micros/op; 1081.9 MB/s (output: 55.2%) snappyuncomp : 0.691 micros/op; 5656.3 MB/s zstdcomp : 15.731 micros/op; 248.3 MB/s (output: 44.1%) zstduncomp : 4.218 micros/op; 926.2 MB/s PiperOrigin-RevId: 509957778
1 year ago
Add support for Zstd-based compression in LevelDB. This change implements support for Zstd-based compression in LevelDB. Building up from the Snappy compression (which has been supported since inception), this change adds Zstd as an alternate compression algorithm. We are implementing this to provide alternative options for users who might have different performance and efficiency requirements. For instance, the Zstandard website (https://facebook.github.io/zstd/) claims that the Zstd algorithm can achieve around 30% higher compression ratios than Snappy, with relatively smaller (~10%) slowdowns in de/compression speeds. Benchmarking results: $ blaze-bin/third_party/leveldb/db_bench LevelDB: version 1.23 Date: Thu Feb 2 18:50:06 2023 CPU: 56 * Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz CPUCache: 35840 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) ------------------------------------------------ fillseq : 2.613 micros/op; 42.3 MB/s fillsync : 3924.432 micros/op; 0.0 MB/s (1000 ops) fillrandom : 3.609 micros/op; 30.7 MB/s overwrite : 4.508 micros/op; 24.5 MB/s readrandom : 6.136 micros/op; (864322 of 1000000 found) readrandom : 5.446 micros/op; (864083 of 1000000 found) readseq : 0.180 micros/op; 613.3 MB/s readreverse : 0.321 micros/op; 344.7 MB/s compact : 827043.000 micros/op; readrandom : 4.603 micros/op; (864105 of 1000000 found) readseq : 0.169 micros/op; 656.3 MB/s readreverse : 0.315 micros/op; 350.8 MB/s fill100K : 854.009 micros/op; 111.7 MB/s (1000 ops) crc32c : 1.227 micros/op; 3184.0 MB/s (4K per op) snappycomp : 3.610 micros/op; 1081.9 MB/s (output: 55.2%) snappyuncomp : 0.691 micros/op; 5656.3 MB/s zstdcomp : 15.731 micros/op; 248.3 MB/s (output: 44.1%) zstduncomp : 4.218 micros/op; 926.2 MB/s PiperOrigin-RevId: 509957778
1 year ago
Add support for Zstd-based compression in LevelDB. This change implements support for Zstd-based compression in LevelDB. Building up from the Snappy compression (which has been supported since inception), this change adds Zstd as an alternate compression algorithm. We are implementing this to provide alternative options for users who might have different performance and efficiency requirements. For instance, the Zstandard website (https://facebook.github.io/zstd/) claims that the Zstd algorithm can achieve around 30% higher compression ratios than Snappy, with relatively smaller (~10%) slowdowns in de/compression speeds. Benchmarking results: $ blaze-bin/third_party/leveldb/db_bench LevelDB: version 1.23 Date: Thu Feb 2 18:50:06 2023 CPU: 56 * Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz CPUCache: 35840 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) ------------------------------------------------ fillseq : 2.613 micros/op; 42.3 MB/s fillsync : 3924.432 micros/op; 0.0 MB/s (1000 ops) fillrandom : 3.609 micros/op; 30.7 MB/s overwrite : 4.508 micros/op; 24.5 MB/s readrandom : 6.136 micros/op; (864322 of 1000000 found) readrandom : 5.446 micros/op; (864083 of 1000000 found) readseq : 0.180 micros/op; 613.3 MB/s readreverse : 0.321 micros/op; 344.7 MB/s compact : 827043.000 micros/op; readrandom : 4.603 micros/op; (864105 of 1000000 found) readseq : 0.169 micros/op; 656.3 MB/s readreverse : 0.315 micros/op; 350.8 MB/s fill100K : 854.009 micros/op; 111.7 MB/s (1000 ops) crc32c : 1.227 micros/op; 3184.0 MB/s (4K per op) snappycomp : 3.610 micros/op; 1081.9 MB/s (output: 55.2%) snappyuncomp : 0.691 micros/op; 5656.3 MB/s zstdcomp : 15.731 micros/op; 248.3 MB/s (output: 44.1%) zstduncomp : 4.218 micros/op; 926.2 MB/s PiperOrigin-RevId: 509957778
1 year 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
4 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 <sys/types.h>
  5. #include <atomic>
  6. #include <cstdio>
  7. #include <cstdlib>
  8. #include "leveldb/cache.h"
  9. #include "leveldb/comparator.h"
  10. #include "leveldb/db.h"
  11. #include "leveldb/env.h"
  12. #include "leveldb/filter_policy.h"
  13. #include "leveldb/write_batch.h"
  14. #include "port/port.h"
  15. #include "util/crc32c.h"
  16. #include "util/histogram.h"
  17. #include "util/mutexlock.h"
  18. #include "util/random.h"
  19. #include "util/testutil.h"
  20. // Comma-separated list of operations to run in the specified order
  21. // Actual benchmarks:
  22. // fillseq -- write N values in sequential key order in async mode
  23. // fillrandom -- write N values in random key order in async mode
  24. // overwrite -- overwrite N values in random key order in async mode
  25. // fillsync -- write N/100 values in random key order in sync mode
  26. // fill100K -- write N/1000 100K values in random order in async mode
  27. // deleteseq -- delete N keys in sequential order
  28. // deleterandom -- delete N keys in random order
  29. // readseq -- read N times sequentially
  30. // readreverse -- read N times in reverse order
  31. // readrandom -- read N times in random order
  32. // readmissing -- read N missing keys in random order
  33. // readhot -- read N times in random order from 1% section of DB
  34. // seekrandom -- N random seeks
  35. // seekordered -- N ordered seeks
  36. // open -- cost of opening a DB
  37. // crc32c -- repeated crc32c of 4K of data
  38. // Meta operations:
  39. // compact -- Compact the entire DB
  40. // stats -- Print DB stats
  41. // sstables -- Print sstable info
  42. // heapprofile -- Dump a heap profile (if supported by this port)
  43. static const char* FLAGS_benchmarks =
  44. "fillseq,"
  45. "fillsync,"
  46. "fillrandom,"
  47. "overwrite,"
  48. "readrandom,"
  49. "readrandom," // Extra run to allow previous compactions to quiesce
  50. "readseq,"
  51. "readreverse,"
  52. "compact,"
  53. "readrandom,"
  54. "readseq,"
  55. "readreverse,"
  56. "fill100K,"
  57. "crc32c,"
  58. "snappycomp,"
  59. "snappyuncomp,"
  60. "zstdcomp,"
  61. "zstduncomp,";
  62. // Number of key/values to place in database
  63. static int FLAGS_num = 1000000;
  64. // Number of read operations to do. If negative, do FLAGS_num reads.
  65. static int FLAGS_reads = -1;
  66. // Number of concurrent threads to run.
  67. static int FLAGS_threads = 1;
  68. // Size of each value
  69. static int FLAGS_value_size = 100;
  70. // Arrange to generate values that shrink to this fraction of
  71. // their original size after compression
  72. static double FLAGS_compression_ratio = 0.5;
  73. // Print histogram of operation timings
  74. static bool FLAGS_histogram = false;
  75. // Count the number of string comparisons performed
  76. static bool FLAGS_comparisons = false;
  77. // Number of bytes to buffer in memtable before compacting
  78. // (initialized to default value by "main")
  79. static int FLAGS_write_buffer_size = 0;
  80. // Number of bytes written to each file.
  81. // (initialized to default value by "main")
  82. static int FLAGS_max_file_size = 0;
  83. // Approximate size of user data packed per block (before compression.
  84. // (initialized to default value by "main")
  85. static int FLAGS_block_size = 0;
  86. // Number of bytes to use as a cache of uncompressed data.
  87. // Negative means use default settings.
  88. static int FLAGS_cache_size = -1;
  89. // Maximum number of files to keep open at the same time (use default if == 0)
  90. static int FLAGS_open_files = 0;
  91. // Bloom filter bits per key.
  92. // Negative means use default settings.
  93. static int FLAGS_bloom_bits = -1;
  94. // Common key prefix length.
  95. static int FLAGS_key_prefix = 0;
  96. // If true, do not destroy the existing database. If you set this
  97. // flag and also specify a benchmark that wants a fresh database, that
  98. // benchmark will fail.
  99. static bool FLAGS_use_existing_db = false;
  100. // If true, reuse existing log/MANIFEST files when re-opening a database.
  101. static bool FLAGS_reuse_logs = false;
  102. // If true, use compression.
  103. static bool FLAGS_compression = true;
  104. // Use the db with the following name.
  105. static const char* FLAGS_db = nullptr;
  106. namespace leveldb {
  107. namespace {
  108. leveldb::Env* g_env = nullptr;
  109. class CountComparator : public Comparator {
  110. public:
  111. CountComparator(const Comparator* wrapped) : wrapped_(wrapped) {}
  112. ~CountComparator() override {}
  113. int Compare(const Slice& a, const Slice& b) const override {
  114. count_.fetch_add(1, std::memory_order_relaxed);
  115. return wrapped_->Compare(a, b);
  116. }
  117. const char* Name() const override { return wrapped_->Name(); }
  118. void FindShortestSeparator(std::string* start,
  119. const Slice& limit) const override {
  120. wrapped_->FindShortestSeparator(start, limit);
  121. }
  122. void FindShortSuccessor(std::string* key) const override {
  123. return wrapped_->FindShortSuccessor(key);
  124. }
  125. size_t comparisons() const { return count_.load(std::memory_order_relaxed); }
  126. void reset() { count_.store(0, std::memory_order_relaxed); }
  127. private:
  128. mutable std::atomic<size_t> count_{0};
  129. const Comparator* const wrapped_;
  130. };
  131. // Helper for quickly generating random data.
  132. class RandomGenerator {
  133. private:
  134. std::string data_;
  135. int pos_;
  136. public:
  137. RandomGenerator() {
  138. // We use a limited amount of data over and over again and ensure
  139. // that it is larger than the compression window (32KB), and also
  140. // large enough to serve all typical value sizes we want to write.
  141. Random rnd(301);
  142. std::string piece;
  143. while (data_.size() < 1048576) {
  144. // Add a short fragment that is as compressible as specified
  145. // by FLAGS_compression_ratio.
  146. test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
  147. data_.append(piece);
  148. }
  149. pos_ = 0;
  150. }
  151. Slice Generate(size_t len) {
  152. if (pos_ + len > data_.size()) {
  153. pos_ = 0;
  154. assert(len < data_.size());
  155. }
  156. pos_ += len;
  157. return Slice(data_.data() + pos_ - len, len);
  158. }
  159. };
  160. class KeyBuffer {
  161. public:
  162. KeyBuffer() {
  163. assert(FLAGS_key_prefix < sizeof(buffer_));
  164. memset(buffer_, 'a', FLAGS_key_prefix);
  165. }
  166. KeyBuffer& operator=(KeyBuffer& other) = delete;
  167. KeyBuffer(KeyBuffer& other) = delete;
  168. void Set(int k) {
  169. std::snprintf(buffer_ + FLAGS_key_prefix,
  170. sizeof(buffer_) - FLAGS_key_prefix, "%016d", k);
  171. }
  172. Slice slice() const { return Slice(buffer_, FLAGS_key_prefix + 16); }
  173. private:
  174. char buffer_[1024];
  175. };
  176. #if defined(__linux)
  177. static Slice TrimSpace(Slice s) {
  178. size_t start = 0;
  179. while (start < s.size() && isspace(s[start])) {
  180. start++;
  181. }
  182. size_t limit = s.size();
  183. while (limit > start && isspace(s[limit - 1])) {
  184. limit--;
  185. }
  186. return Slice(s.data() + start, limit - start);
  187. }
  188. #endif
  189. static void AppendWithSpace(std::string* str, Slice msg) {
  190. if (msg.empty()) return;
  191. if (!str->empty()) {
  192. str->push_back(' ');
  193. }
  194. str->append(msg.data(), msg.size());
  195. }
  196. class Stats {
  197. private:
  198. double start_;
  199. double finish_;
  200. double seconds_;
  201. int done_;
  202. int next_report_;
  203. int64_t bytes_;
  204. double last_op_finish_;
  205. Histogram hist_;
  206. std::string message_;
  207. public:
  208. Stats() { Start(); }
  209. void Start() {
  210. next_report_ = 100;
  211. hist_.Clear();
  212. done_ = 0;
  213. bytes_ = 0;
  214. seconds_ = 0;
  215. message_.clear();
  216. start_ = finish_ = last_op_finish_ = g_env->NowMicros();
  217. }
  218. void Merge(const Stats& other) {
  219. hist_.Merge(other.hist_);
  220. done_ += other.done_;
  221. bytes_ += other.bytes_;
  222. seconds_ += other.seconds_;
  223. if (other.start_ < start_) start_ = other.start_;
  224. if (other.finish_ > finish_) finish_ = other.finish_;
  225. // Just keep the messages from one thread
  226. if (message_.empty()) message_ = other.message_;
  227. }
  228. void Stop() {
  229. finish_ = g_env->NowMicros();
  230. seconds_ = (finish_ - start_) * 1e-6;
  231. }
  232. void AddMessage(Slice msg) { AppendWithSpace(&message_, msg); }
  233. void FinishedSingleOp() {
  234. if (FLAGS_histogram) {
  235. double now = g_env->NowMicros();
  236. double micros = now - last_op_finish_;
  237. hist_.Add(micros);
  238. if (micros > 20000) {
  239. std::fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
  240. std::fflush(stderr);
  241. }
  242. last_op_finish_ = now;
  243. }
  244. done_++;
  245. if (done_ >= next_report_) {
  246. if (next_report_ < 1000)
  247. next_report_ += 100;
  248. else if (next_report_ < 5000)
  249. next_report_ += 500;
  250. else if (next_report_ < 10000)
  251. next_report_ += 1000;
  252. else if (next_report_ < 50000)
  253. next_report_ += 5000;
  254. else if (next_report_ < 100000)
  255. next_report_ += 10000;
  256. else if (next_report_ < 500000)
  257. next_report_ += 50000;
  258. else
  259. next_report_ += 100000;
  260. std::fprintf(stderr, "... finished %d ops%30s\r", done_, "");
  261. std::fflush(stderr);
  262. }
  263. }
  264. void AddBytes(int64_t n) { bytes_ += n; }
  265. void Report(const Slice& name) {
  266. // Pretend at least one op was done in case we are running a benchmark
  267. // that does not call FinishedSingleOp().
  268. if (done_ < 1) done_ = 1;
  269. std::string extra;
  270. if (bytes_ > 0) {
  271. // Rate is computed on actual elapsed time, not the sum of per-thread
  272. // elapsed times.
  273. double elapsed = (finish_ - start_) * 1e-6;
  274. char rate[100];
  275. std::snprintf(rate, sizeof(rate), "%6.1f MB/s",
  276. (bytes_ / 1048576.0) / elapsed);
  277. extra = rate;
  278. }
  279. AppendWithSpace(&extra, message_);
  280. std::fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
  281. name.ToString().c_str(), seconds_ * 1e6 / done_,
  282. (extra.empty() ? "" : " "), extra.c_str());
  283. if (FLAGS_histogram) {
  284. std::fprintf(stdout, "Microseconds per op:\n%s\n",
  285. hist_.ToString().c_str());
  286. }
  287. std::fflush(stdout);
  288. }
  289. };
  290. // State shared by all concurrent executions of the same benchmark.
  291. struct SharedState {
  292. port::Mutex mu;
  293. port::CondVar cv GUARDED_BY(mu);
  294. int total GUARDED_BY(mu);
  295. // Each thread goes through the following states:
  296. // (1) initializing
  297. // (2) waiting for others to be initialized
  298. // (3) running
  299. // (4) done
  300. int num_initialized GUARDED_BY(mu);
  301. int num_done GUARDED_BY(mu);
  302. bool start GUARDED_BY(mu);
  303. SharedState(int total)
  304. : cv(&mu), total(total), num_initialized(0), num_done(0), start(false) {}
  305. };
  306. // Per-thread state for concurrent executions of the same benchmark.
  307. struct ThreadState {
  308. int tid; // 0..n-1 when running in n threads
  309. Random rand; // Has different seeds for different threads
  310. Stats stats;
  311. SharedState* shared;
  312. ThreadState(int index, int seed) : tid(index), rand(seed), shared(nullptr) {}
  313. };
  314. void Compress(
  315. ThreadState* thread, std::string name,
  316. std::function<bool(const char*, size_t, std::string*)> compress_func) {
  317. RandomGenerator gen;
  318. Slice input = gen.Generate(Options().block_size);
  319. int64_t bytes = 0;
  320. int64_t produced = 0;
  321. bool ok = true;
  322. std::string compressed;
  323. while (ok && bytes < 1024 * 1048576) { // Compress 1G
  324. ok = compress_func(input.data(), input.size(), &compressed);
  325. produced += compressed.size();
  326. bytes += input.size();
  327. thread->stats.FinishedSingleOp();
  328. }
  329. if (!ok) {
  330. thread->stats.AddMessage("(" + name + " failure)");
  331. } else {
  332. char buf[100];
  333. std::snprintf(buf, sizeof(buf), "(output: %.1f%%)",
  334. (produced * 100.0) / bytes);
  335. thread->stats.AddMessage(buf);
  336. thread->stats.AddBytes(bytes);
  337. }
  338. }
  339. void Uncompress(
  340. ThreadState* thread, std::string name,
  341. std::function<bool(const char*, size_t, std::string*)> compress_func,
  342. std::function<bool(const char*, size_t, char*)> uncompress_func) {
  343. RandomGenerator gen;
  344. Slice input = gen.Generate(Options().block_size);
  345. std::string compressed;
  346. bool ok = compress_func(input.data(), input.size(), &compressed);
  347. int64_t bytes = 0;
  348. char* uncompressed = new char[input.size()];
  349. while (ok && bytes < 1024 * 1048576) { // Compress 1G
  350. ok = uncompress_func(compressed.data(), compressed.size(), uncompressed);
  351. bytes += input.size();
  352. thread->stats.FinishedSingleOp();
  353. }
  354. delete[] uncompressed;
  355. if (!ok) {
  356. thread->stats.AddMessage("(" + name + " failure)");
  357. } else {
  358. thread->stats.AddBytes(bytes);
  359. }
  360. }
  361. } // namespace
  362. class Benchmark {
  363. private:
  364. Cache* cache_;
  365. const FilterPolicy* filter_policy_;
  366. DB* db_;
  367. int num_;
  368. int value_size_;
  369. int entries_per_batch_;
  370. WriteOptions write_options_;
  371. int reads_;
  372. int heap_counter_;
  373. CountComparator count_comparator_;
  374. int total_thread_count_;
  375. void PrintHeader() {
  376. const int kKeySize = 16 + FLAGS_key_prefix;
  377. PrintEnvironment();
  378. std::fprintf(stdout, "Keys: %d bytes each\n", kKeySize);
  379. std::fprintf(
  380. stdout, "Values: %d bytes each (%d bytes after compression)\n",
  381. FLAGS_value_size,
  382. static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));
  383. std::fprintf(stdout, "Entries: %d\n", num_);
  384. std::fprintf(stdout, "RawSize: %.1f MB (estimated)\n",
  385. ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) /
  386. 1048576.0));
  387. std::fprintf(
  388. stdout, "FileSize: %.1f MB (estimated)\n",
  389. (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) /
  390. 1048576.0));
  391. PrintWarnings();
  392. std::fprintf(stdout, "------------------------------------------------\n");
  393. }
  394. void PrintWarnings() {
  395. #if defined(__GNUC__) && !defined(__OPTIMIZE__)
  396. std::fprintf(
  397. stdout,
  398. "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
  399. #endif
  400. #ifndef NDEBUG
  401. std::fprintf(
  402. stdout,
  403. "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
  404. #endif
  405. // See if snappy is working by attempting to compress a compressible string
  406. const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
  407. std::string compressed;
  408. if (!port::Snappy_Compress(text, sizeof(text), &compressed)) {
  409. std::fprintf(stdout, "WARNING: Snappy compression is not enabled\n");
  410. } else if (compressed.size() >= sizeof(text)) {
  411. std::fprintf(stdout, "WARNING: Snappy compression is not effective\n");
  412. }
  413. }
  414. void PrintEnvironment() {
  415. std::fprintf(stderr, "LevelDB: version %d.%d\n", kMajorVersion,
  416. kMinorVersion);
  417. #if defined(__linux)
  418. time_t now = time(nullptr);
  419. std::fprintf(stderr, "Date: %s",
  420. ctime(&now)); // ctime() adds newline
  421. FILE* cpuinfo = std::fopen("/proc/cpuinfo", "r");
  422. if (cpuinfo != nullptr) {
  423. char line[1000];
  424. int num_cpus = 0;
  425. std::string cpu_type;
  426. std::string cache_size;
  427. while (fgets(line, sizeof(line), cpuinfo) != nullptr) {
  428. const char* sep = strchr(line, ':');
  429. if (sep == nullptr) {
  430. continue;
  431. }
  432. Slice key = TrimSpace(Slice(line, sep - 1 - line));
  433. Slice val = TrimSpace(Slice(sep + 1));
  434. if (key == "model name") {
  435. ++num_cpus;
  436. cpu_type = val.ToString();
  437. } else if (key == "cache size") {
  438. cache_size = val.ToString();
  439. }
  440. }
  441. std::fclose(cpuinfo);
  442. std::fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str());
  443. std::fprintf(stderr, "CPUCache: %s\n", cache_size.c_str());
  444. }
  445. #endif
  446. }
  447. public:
  448. Benchmark()
  449. : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : nullptr),
  450. filter_policy_(FLAGS_bloom_bits >= 0
  451. ? NewBloomFilterPolicy(FLAGS_bloom_bits)
  452. : nullptr),
  453. db_(nullptr),
  454. num_(FLAGS_num),
  455. value_size_(FLAGS_value_size),
  456. entries_per_batch_(1),
  457. reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
  458. heap_counter_(0),
  459. count_comparator_(BytewiseComparator()),
  460. total_thread_count_(0) {
  461. std::vector<std::string> files;
  462. g_env->GetChildren(FLAGS_db, &files);
  463. for (size_t i = 0; i < files.size(); i++) {
  464. if (Slice(files[i]).starts_with("heap-")) {
  465. g_env->RemoveFile(std::string(FLAGS_db) + "/" + files[i]);
  466. }
  467. }
  468. if (!FLAGS_use_existing_db) {
  469. DestroyDB(FLAGS_db, Options());
  470. }
  471. }
  472. ~Benchmark() {
  473. delete db_;
  474. delete cache_;
  475. delete filter_policy_;
  476. }
  477. void Run() {
  478. PrintHeader();
  479. Open();
  480. const char* benchmarks = FLAGS_benchmarks;
  481. while (benchmarks != nullptr) {
  482. const char* sep = strchr(benchmarks, ',');
  483. Slice name;
  484. if (sep == nullptr) {
  485. name = benchmarks;
  486. benchmarks = nullptr;
  487. } else {
  488. name = Slice(benchmarks, sep - benchmarks);
  489. benchmarks = sep + 1;
  490. }
  491. // Reset parameters that may be overridden below
  492. num_ = FLAGS_num;
  493. reads_ = (FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads);
  494. value_size_ = FLAGS_value_size;
  495. entries_per_batch_ = 1;
  496. write_options_ = WriteOptions();
  497. void (Benchmark::*method)(ThreadState*) = nullptr;
  498. bool fresh_db = false;
  499. int num_threads = FLAGS_threads;
  500. if (name == Slice("open")) {
  501. method = &Benchmark::OpenBench;
  502. num_ /= 10000;
  503. if (num_ < 1) num_ = 1;
  504. } else if (name == Slice("fillseq")) {
  505. fresh_db = true;
  506. method = &Benchmark::WriteSeq;
  507. } else if (name == Slice("fillbatch")) {
  508. fresh_db = true;
  509. entries_per_batch_ = 1000;
  510. method = &Benchmark::WriteSeq;
  511. } else if (name == Slice("fillrandom")) {
  512. fresh_db = true;
  513. method = &Benchmark::WriteRandom;
  514. } else if (name == Slice("overwrite")) {
  515. fresh_db = false;
  516. method = &Benchmark::WriteRandom;
  517. } else if (name == Slice("fillsync")) {
  518. fresh_db = true;
  519. num_ /= 1000;
  520. write_options_.sync = true;
  521. method = &Benchmark::WriteRandom;
  522. } else if (name == Slice("fill100K")) {
  523. fresh_db = true;
  524. num_ /= 1000;
  525. value_size_ = 100 * 1000;
  526. method = &Benchmark::WriteRandom;
  527. } else if (name == Slice("readseq")) {
  528. method = &Benchmark::ReadSequential;
  529. } else if (name == Slice("readreverse")) {
  530. method = &Benchmark::ReadReverse;
  531. } else if (name == Slice("readrandom")) {
  532. method = &Benchmark::ReadRandom;
  533. } else if (name == Slice("readmissing")) {
  534. method = &Benchmark::ReadMissing;
  535. } else if (name == Slice("seekrandom")) {
  536. method = &Benchmark::SeekRandom;
  537. } else if (name == Slice("seekordered")) {
  538. method = &Benchmark::SeekOrdered;
  539. } else if (name == Slice("readhot")) {
  540. method = &Benchmark::ReadHot;
  541. } else if (name == Slice("readrandomsmall")) {
  542. reads_ /= 1000;
  543. method = &Benchmark::ReadRandom;
  544. } else if (name == Slice("deleteseq")) {
  545. method = &Benchmark::DeleteSeq;
  546. } else if (name == Slice("deleterandom")) {
  547. method = &Benchmark::DeleteRandom;
  548. } else if (name == Slice("readwhilewriting")) {
  549. num_threads++; // Add extra thread for writing
  550. method = &Benchmark::ReadWhileWriting;
  551. } else if (name == Slice("compact")) {
  552. method = &Benchmark::Compact;
  553. } else if (name == Slice("crc32c")) {
  554. method = &Benchmark::Crc32c;
  555. } else if (name == Slice("snappycomp")) {
  556. method = &Benchmark::SnappyCompress;
  557. } else if (name == Slice("snappyuncomp")) {
  558. method = &Benchmark::SnappyUncompress;
  559. } else if (name == Slice("zstdcomp")) {
  560. method = &Benchmark::ZstdCompress;
  561. } else if (name == Slice("zstduncomp")) {
  562. method = &Benchmark::ZstdUncompress;
  563. } else if (name == Slice("heapprofile")) {
  564. HeapProfile();
  565. } else if (name == Slice("stats")) {
  566. PrintStats("leveldb.stats");
  567. } else if (name == Slice("sstables")) {
  568. PrintStats("leveldb.sstables");
  569. } else {
  570. if (!name.empty()) { // No error message for empty name
  571. std::fprintf(stderr, "unknown benchmark '%s'\n",
  572. name.ToString().c_str());
  573. }
  574. }
  575. if (fresh_db) {
  576. if (FLAGS_use_existing_db) {
  577. std::fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n",
  578. name.ToString().c_str());
  579. method = nullptr;
  580. } else {
  581. delete db_;
  582. db_ = nullptr;
  583. DestroyDB(FLAGS_db, Options());
  584. Open();
  585. }
  586. }
  587. if (method != nullptr) {
  588. RunBenchmark(num_threads, name, method);
  589. }
  590. }
  591. }
  592. private:
  593. struct ThreadArg {
  594. Benchmark* bm;
  595. SharedState* shared;
  596. ThreadState* thread;
  597. void (Benchmark::*method)(ThreadState*);
  598. };
  599. static void ThreadBody(void* v) {
  600. ThreadArg* arg = reinterpret_cast<ThreadArg*>(v);
  601. SharedState* shared = arg->shared;
  602. ThreadState* thread = arg->thread;
  603. {
  604. MutexLock l(&shared->mu);
  605. shared->num_initialized++;
  606. if (shared->num_initialized >= shared->total) {
  607. shared->cv.SignalAll();
  608. }
  609. while (!shared->start) {
  610. shared->cv.Wait();
  611. }
  612. }
  613. thread->stats.Start();
  614. (arg->bm->*(arg->method))(thread);
  615. thread->stats.Stop();
  616. {
  617. MutexLock l(&shared->mu);
  618. shared->num_done++;
  619. if (shared->num_done >= shared->total) {
  620. shared->cv.SignalAll();
  621. }
  622. }
  623. }
  624. void RunBenchmark(int n, Slice name,
  625. void (Benchmark::*method)(ThreadState*)) {
  626. SharedState shared(n);
  627. ThreadArg* arg = new ThreadArg[n];
  628. for (int i = 0; i < n; i++) {
  629. arg[i].bm = this;
  630. arg[i].method = method;
  631. arg[i].shared = &shared;
  632. ++total_thread_count_;
  633. // Seed the thread's random state deterministically based upon thread
  634. // creation across all benchmarks. This ensures that the seeds are unique
  635. // but reproducible when rerunning the same set of benchmarks.
  636. arg[i].thread = new ThreadState(i, /*seed=*/1000 + total_thread_count_);
  637. arg[i].thread->shared = &shared;
  638. g_env->StartThread(ThreadBody, &arg[i]);
  639. }
  640. shared.mu.Lock();
  641. while (shared.num_initialized < n) {
  642. shared.cv.Wait();
  643. }
  644. shared.start = true;
  645. shared.cv.SignalAll();
  646. while (shared.num_done < n) {
  647. shared.cv.Wait();
  648. }
  649. shared.mu.Unlock();
  650. for (int i = 1; i < n; i++) {
  651. arg[0].thread->stats.Merge(arg[i].thread->stats);
  652. }
  653. arg[0].thread->stats.Report(name);
  654. if (FLAGS_comparisons) {
  655. fprintf(stdout, "Comparisons: %zu\n", count_comparator_.comparisons());
  656. count_comparator_.reset();
  657. fflush(stdout);
  658. }
  659. for (int i = 0; i < n; i++) {
  660. delete arg[i].thread;
  661. }
  662. delete[] arg;
  663. }
  664. void Crc32c(ThreadState* thread) {
  665. // Checksum about 500MB of data total
  666. const int size = 4096;
  667. const char* label = "(4K per op)";
  668. std::string data(size, 'x');
  669. int64_t bytes = 0;
  670. uint32_t crc = 0;
  671. while (bytes < 500 * 1048576) {
  672. crc = crc32c::Value(data.data(), size);
  673. thread->stats.FinishedSingleOp();
  674. bytes += size;
  675. }
  676. // Print so result is not dead
  677. std::fprintf(stderr, "... crc=0x%x\r", static_cast<unsigned int>(crc));
  678. thread->stats.AddBytes(bytes);
  679. thread->stats.AddMessage(label);
  680. }
  681. void SnappyCompress(ThreadState* thread) {
  682. Compress(thread, "snappy", &port::Snappy_Compress);
  683. }
  684. void SnappyUncompress(ThreadState* thread) {
  685. Uncompress(thread, "snappy", &port::Snappy_Compress,
  686. &port::Snappy_Uncompress);
  687. }
  688. void ZstdCompress(ThreadState* thread) {
  689. Compress(thread, "zstd", &port::Zstd_Compress);
  690. }
  691. void ZstdUncompress(ThreadState* thread) {
  692. Uncompress(thread, "zstd", &port::Zstd_Compress, &port::Zstd_Uncompress);
  693. }
  694. void Open() {
  695. assert(db_ == nullptr);
  696. Options options;
  697. options.env = g_env;
  698. options.create_if_missing = !FLAGS_use_existing_db;
  699. options.block_cache = cache_;
  700. options.write_buffer_size = FLAGS_write_buffer_size;
  701. options.max_file_size = FLAGS_max_file_size;
  702. options.block_size = FLAGS_block_size;
  703. if (FLAGS_comparisons) {
  704. options.comparator = &count_comparator_;
  705. }
  706. options.max_open_files = FLAGS_open_files;
  707. options.filter_policy = filter_policy_;
  708. options.reuse_logs = FLAGS_reuse_logs;
  709. options.compression =
  710. FLAGS_compression ? kSnappyCompression : kNoCompression;
  711. Status s = DB::Open(options, FLAGS_db, &db_);
  712. if (!s.ok()) {
  713. std::fprintf(stderr, "open error: %s\n", s.ToString().c_str());
  714. std::exit(1);
  715. }
  716. }
  717. void OpenBench(ThreadState* thread) {
  718. for (int i = 0; i < num_; i++) {
  719. delete db_;
  720. Open();
  721. thread->stats.FinishedSingleOp();
  722. }
  723. }
  724. void WriteSeq(ThreadState* thread) { DoWrite(thread, true); }
  725. void WriteRandom(ThreadState* thread) { DoWrite(thread, false); }
  726. void DoWrite(ThreadState* thread, bool seq) {
  727. if (num_ != FLAGS_num) {
  728. char msg[100];
  729. std::snprintf(msg, sizeof(msg), "(%d ops)", num_);
  730. thread->stats.AddMessage(msg);
  731. }
  732. RandomGenerator gen;
  733. WriteBatch batch;
  734. Status s;
  735. int64_t bytes = 0;
  736. KeyBuffer key;
  737. for (int i = 0; i < num_; i += entries_per_batch_) {
  738. batch.Clear();
  739. for (int j = 0; j < entries_per_batch_; j++) {
  740. const int k = seq ? i + j : thread->rand.Uniform(FLAGS_num);
  741. key.Set(k);
  742. batch.Put(key.slice(), gen.Generate(value_size_));
  743. bytes += value_size_ + key.slice().size();
  744. thread->stats.FinishedSingleOp();
  745. }
  746. s = db_->Write(write_options_, &batch);
  747. if (!s.ok()) {
  748. std::fprintf(stderr, "put error: %s\n", s.ToString().c_str());
  749. std::exit(1);
  750. }
  751. }
  752. thread->stats.AddBytes(bytes);
  753. }
  754. void ReadSequential(ThreadState* thread) {
  755. Iterator* iter = db_->NewIterator(ReadOptions());
  756. int i = 0;
  757. int64_t bytes = 0;
  758. for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) {
  759. bytes += iter->key().size() + iter->value().size();
  760. thread->stats.FinishedSingleOp();
  761. ++i;
  762. }
  763. delete iter;
  764. thread->stats.AddBytes(bytes);
  765. }
  766. void ReadReverse(ThreadState* thread) {
  767. Iterator* iter = db_->NewIterator(ReadOptions());
  768. int i = 0;
  769. int64_t bytes = 0;
  770. for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) {
  771. bytes += iter->key().size() + iter->value().size();
  772. thread->stats.FinishedSingleOp();
  773. ++i;
  774. }
  775. delete iter;
  776. thread->stats.AddBytes(bytes);
  777. }
  778. void ReadRandom(ThreadState* thread) {
  779. ReadOptions options;
  780. std::string value;
  781. int found = 0;
  782. KeyBuffer key;
  783. for (int i = 0; i < reads_; i++) {
  784. const int k = thread->rand.Uniform(FLAGS_num);
  785. key.Set(k);
  786. if (db_->Get(options, key.slice(), &value).ok()) {
  787. found++;
  788. }
  789. thread->stats.FinishedSingleOp();
  790. }
  791. char msg[100];
  792. std::snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_);
  793. thread->stats.AddMessage(msg);
  794. }
  795. void ReadMissing(ThreadState* thread) {
  796. ReadOptions options;
  797. std::string value;
  798. KeyBuffer key;
  799. for (int i = 0; i < reads_; i++) {
  800. const int k = thread->rand.Uniform(FLAGS_num);
  801. key.Set(k);
  802. Slice s = Slice(key.slice().data(), key.slice().size() - 1);
  803. db_->Get(options, s, &value);
  804. thread->stats.FinishedSingleOp();
  805. }
  806. }
  807. void ReadHot(ThreadState* thread) {
  808. ReadOptions options;
  809. std::string value;
  810. const int range = (FLAGS_num + 99) / 100;
  811. KeyBuffer key;
  812. for (int i = 0; i < reads_; i++) {
  813. const int k = thread->rand.Uniform(range);
  814. key.Set(k);
  815. db_->Get(options, key.slice(), &value);
  816. thread->stats.FinishedSingleOp();
  817. }
  818. }
  819. void SeekRandom(ThreadState* thread) {
  820. ReadOptions options;
  821. int found = 0;
  822. KeyBuffer key;
  823. for (int i = 0; i < reads_; i++) {
  824. Iterator* iter = db_->NewIterator(options);
  825. const int k = thread->rand.Uniform(FLAGS_num);
  826. key.Set(k);
  827. iter->Seek(key.slice());
  828. if (iter->Valid() && iter->key() == key.slice()) found++;
  829. delete iter;
  830. thread->stats.FinishedSingleOp();
  831. }
  832. char msg[100];
  833. snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_);
  834. thread->stats.AddMessage(msg);
  835. }
  836. void SeekOrdered(ThreadState* thread) {
  837. ReadOptions options;
  838. Iterator* iter = db_->NewIterator(options);
  839. int found = 0;
  840. int k = 0;
  841. KeyBuffer key;
  842. for (int i = 0; i < reads_; i++) {
  843. k = (k + (thread->rand.Uniform(100))) % FLAGS_num;
  844. key.Set(k);
  845. iter->Seek(key.slice());
  846. if (iter->Valid() && iter->key() == key.slice()) found++;
  847. thread->stats.FinishedSingleOp();
  848. }
  849. delete iter;
  850. char msg[100];
  851. std::snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_);
  852. thread->stats.AddMessage(msg);
  853. }
  854. void DoDelete(ThreadState* thread, bool seq) {
  855. RandomGenerator gen;
  856. WriteBatch batch;
  857. Status s;
  858. KeyBuffer key;
  859. for (int i = 0; i < num_; i += entries_per_batch_) {
  860. batch.Clear();
  861. for (int j = 0; j < entries_per_batch_; j++) {
  862. const int k = seq ? i + j : (thread->rand.Uniform(FLAGS_num));
  863. key.Set(k);
  864. batch.Delete(key.slice());
  865. thread->stats.FinishedSingleOp();
  866. }
  867. s = db_->Write(write_options_, &batch);
  868. if (!s.ok()) {
  869. std::fprintf(stderr, "del error: %s\n", s.ToString().c_str());
  870. std::exit(1);
  871. }
  872. }
  873. }
  874. void DeleteSeq(ThreadState* thread) { DoDelete(thread, true); }
  875. void DeleteRandom(ThreadState* thread) { DoDelete(thread, false); }
  876. void ReadWhileWriting(ThreadState* thread) {
  877. if (thread->tid > 0) {
  878. ReadRandom(thread);
  879. } else {
  880. // Special thread that keeps writing until other threads are done.
  881. RandomGenerator gen;
  882. KeyBuffer key;
  883. while (true) {
  884. {
  885. MutexLock l(&thread->shared->mu);
  886. if (thread->shared->num_done + 1 >= thread->shared->num_initialized) {
  887. // Other threads have finished
  888. break;
  889. }
  890. }
  891. const int k = thread->rand.Uniform(FLAGS_num);
  892. key.Set(k);
  893. Status s =
  894. db_->Put(write_options_, key.slice(), gen.Generate(value_size_));
  895. if (!s.ok()) {
  896. std::fprintf(stderr, "put error: %s\n", s.ToString().c_str());
  897. std::exit(1);
  898. }
  899. }
  900. // Do not count any of the preceding work/delay in stats.
  901. thread->stats.Start();
  902. }
  903. }
  904. void Compact(ThreadState* thread) { db_->CompactRange(nullptr, nullptr); }
  905. void PrintStats(const char* key) {
  906. std::string stats;
  907. if (!db_->GetProperty(key, &stats)) {
  908. stats = "(failed)";
  909. }
  910. std::fprintf(stdout, "\n%s\n", stats.c_str());
  911. }
  912. static void WriteToFile(void* arg, const char* buf, int n) {
  913. reinterpret_cast<WritableFile*>(arg)->Append(Slice(buf, n));
  914. }
  915. void HeapProfile() {
  916. char fname[100];
  917. std::snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db,
  918. ++heap_counter_);
  919. WritableFile* file;
  920. Status s = g_env->NewWritableFile(fname, &file);
  921. if (!s.ok()) {
  922. std::fprintf(stderr, "%s\n", s.ToString().c_str());
  923. return;
  924. }
  925. bool ok = port::GetHeapProfile(WriteToFile, file);
  926. delete file;
  927. if (!ok) {
  928. std::fprintf(stderr, "heap profiling not supported\n");
  929. g_env->RemoveFile(fname);
  930. }
  931. }
  932. };
  933. } // namespace leveldb
  934. int main(int argc, char** argv) {
  935. FLAGS_write_buffer_size = leveldb::Options().write_buffer_size;
  936. FLAGS_max_file_size = leveldb::Options().max_file_size;
  937. FLAGS_block_size = leveldb::Options().block_size;
  938. FLAGS_open_files = leveldb::Options().max_open_files;
  939. std::string default_db_path;
  940. for (int i = 1; i < argc; i++) {
  941. double d;
  942. int n;
  943. char junk;
  944. if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
  945. FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
  946. } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
  947. FLAGS_compression_ratio = d;
  948. } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
  949. (n == 0 || n == 1)) {
  950. FLAGS_histogram = n;
  951. } else if (sscanf(argv[i], "--comparisons=%d%c", &n, &junk) == 1 &&
  952. (n == 0 || n == 1)) {
  953. FLAGS_comparisons = n;
  954. } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 &&
  955. (n == 0 || n == 1)) {
  956. FLAGS_use_existing_db = n;
  957. } else if (sscanf(argv[i], "--reuse_logs=%d%c", &n, &junk) == 1 &&
  958. (n == 0 || n == 1)) {
  959. FLAGS_reuse_logs = n;
  960. } else if (sscanf(argv[i], "--compression=%d%c", &n, &junk) == 1 &&
  961. (n == 0 || n == 1)) {
  962. FLAGS_compression = n;
  963. } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
  964. FLAGS_num = n;
  965. } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
  966. FLAGS_reads = n;
  967. } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) {
  968. FLAGS_threads = n;
  969. } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
  970. FLAGS_value_size = n;
  971. } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) {
  972. FLAGS_write_buffer_size = n;
  973. } else if (sscanf(argv[i], "--max_file_size=%d%c", &n, &junk) == 1) {
  974. FLAGS_max_file_size = n;
  975. } else if (sscanf(argv[i], "--block_size=%d%c", &n, &junk) == 1) {
  976. FLAGS_block_size = n;
  977. } else if (sscanf(argv[i], "--key_prefix=%d%c", &n, &junk) == 1) {
  978. FLAGS_key_prefix = n;
  979. } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) {
  980. FLAGS_cache_size = n;
  981. } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) {
  982. FLAGS_bloom_bits = n;
  983. } else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) {
  984. FLAGS_open_files = n;
  985. } else if (strncmp(argv[i], "--db=", 5) == 0) {
  986. FLAGS_db = argv[i] + 5;
  987. } else {
  988. std::fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
  989. std::exit(1);
  990. }
  991. }
  992. leveldb::g_env = leveldb::Env::Default();
  993. // Choose a location for the test database if none given with --db=<path>
  994. if (FLAGS_db == nullptr) {
  995. leveldb::g_env->GetTestDirectory(&default_db_path);
  996. default_db_path += "/dbbench";
  997. FLAGS_db = default_db_path.c_str();
  998. }
  999. leveldb::Benchmark benchmark;
  1000. benchmark.Run();
  1001. return 0;
  1002. }