Configurable multiscan IO coalescing threshold (#13886)

Summary:
Add a new filed `io_coalesce_threshold` to MultiScanArgs to make IO coalescing threshold configurable.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/13886

Test Plan:
db_bench showing less IO requests with higher io_coalesce_threshold
```
Single L0 file, iterator uses BlockBasedTableIterator directly, skipping LevelIterator

DB Set up: ./db_bench --benchmarks="fillseq,compact" --disable_wal=1 --threads=1 --num_levels=1 --compaction_style=2 --fifo_compaction_max_table_files_size_mb=1000 --write_buffer_size=268435456

./db_bench --db="/tmp/rocksdbtest-543376/dbbench" --use_existing_db=1 --benchmarks=multiscan --disable_auto_compactions=1 --seek_nexts=100 --threads=32 --duration=10 --statistics=1 --use_direct_reads=1 ..

--multiscan_coalesce_threshold=0
rocksdb.non.last.level.read.bytes COUNT : 54591304136
rocksdb.non.last.level.read.count COUNT : 7680204
multiscan    :     397.197 micros/op 79401 ops/sec 10.377 seconds 823968 operations; (multscans:24999)

--multiscan_coalesce_threshold=16384
rocksdb.non.last.level.read.bytes COUNT : 95960989272
rocksdb.non.last.level.read.count COUNT : 912008
multiscan    :     389.099 micros/op 81064 ops/sec 10.312 seconds 835968 operations; (multscans:25999)

--multiscan_coalesce_threshold=163840
rocksdb.non.last.level.read.bytes COUNT : 98805008718
rocksdb.non.last.level.read.count COUNT : 827893
multiscan    :     392.831 micros/op 80357 ops/sec 10.353 seconds 831968 operations; (multscans:25999)

DB with multiple files in a level, iterator will use LevelIterator
./db_bench --benchmarks="fillseq,compact" --disable_wal=1 --threads=1 --num_levels=6 --num=10000000

./db_bench --db="/tmp/rocksdbtest-543376/dbbench" --use_existing_db=1 --benchmarks=multiscan --disable_auto_compactions=1 --seek_nexts=100 --threads=32 --duration=10 --statistics=1 --use_direct_reads=1 --num=10000000

--multiscan_coalesce_threshold=0
multiscan    :    1161.734 micros/op 26995 ops/sec 10.667 seconds 287968 operations; (multscans:8999)
rocksdb.non.last.level.read.bytes COUNT : 23917753523
rocksdb.non.last.level.read.count COUNT : 2868907

--multiscan_coalesce_threshold=16384
rocksdb.non.last.level.read.bytes COUNT : 35022281853
rocksdb.non.last.level.read.count COUNT : 287375
multiscan    :    1195.336 micros/op 26265 ops/sec 10.850 seconds 284968 operations; (multscans:8999)

```

Reviewed By: anand1976

Differential Revision: D80381441

Pulled By: cbi42

fbshipit-source-id: 57cc67df4a808e27c3a48ddf3ef6907bec131ee9
This commit is contained in:
Changyu Bi
2025-08-18 10:56:16 -07:00
committed by Facebook GitHub Bot
parent 84f814454a
commit 618f660eab
5 changed files with 21 additions and 6 deletions
+5 -1
View File
@@ -1144,6 +1144,10 @@ class LevelIterator final : public InternalIterator {
}
}
}
// Propagate io colaescing threshold
for (auto& file_to_arg : *file_to_scan_opts_) {
file_to_arg.second.io_coalesce_threshold = so->io_coalesce_threshold;
}
}
private:
@@ -6491,7 +6495,7 @@ Status VersionSet::ReduceNumberOfLevels(const std::string& dbname,
nullptr /*BlockCacheTracer*/, nullptr /*IOTracer*/,
/*db_id*/ "",
/*db_session_id*/ "", options->daily_offpeak_time_utc,
/*error_handler_*/ nullptr, /*read_only=*/false);
/*error_handler_*/ nullptr, /*unchanging=*/false);
Status status;
std::vector<ColumnFamilyDescriptor> dummy;
+7 -1
View File
@@ -1789,14 +1789,17 @@ class MultiScanArgs {
MultiScanArgs(const MultiScanArgs& other) {
comp_ = other.comp_;
original_ranges_ = other.original_ranges_;
io_coalesce_threshold = other.io_coalesce_threshold;
}
MultiScanArgs(MultiScanArgs&& other) noexcept
: comp_(other.comp_),
: io_coalesce_threshold(other.io_coalesce_threshold),
comp_(other.comp_),
original_ranges_(std::move(other.original_ranges_)) {}
MultiScanArgs& operator=(const MultiScanArgs& other) {
comp_ = other.comp_;
original_ranges_ = other.original_ranges_;
io_coalesce_threshold = other.io_coalesce_threshold;
return *this;
}
@@ -1804,6 +1807,7 @@ class MultiScanArgs {
if (this != &other) {
comp_ = other.comp_;
original_ranges_ = std::move(other.original_ranges_);
io_coalesce_threshold = other.io_coalesce_threshold;
}
return *this;
}
@@ -1843,6 +1847,8 @@ class MultiScanArgs {
return original_ranges_;
}
uint64_t io_coalesce_threshold = 16 << 10; // 16KB by default
private:
// The comparator used for ordering ranges
const Comparator* comp_;
@@ -1067,9 +1067,6 @@ void BlockBasedTableIterator::Prepare(const MultiScanArgs* multiscan_opts) {
// Each member in the vector is an index into blocks_to_prepare.
std::vector<std::vector<size_t>> collapsed_blocks_to_read(1);
// TODO: make this threshold configurable
constexpr size_t kCoalesceThreshold = 16 << 10; // 16KB
for (const auto& block_idx : blocks_to_read) {
if (!collapsed_blocks_to_read.back().empty()) {
// Check if we can coalesce.
@@ -1080,7 +1077,8 @@ void BlockBasedTableIterator::Prepare(const MultiScanArgs* multiscan_opts) {
BlockBasedTable::BlockSizeWithTrailer(last_block);
uint64_t current_start = blocks_to_prepare[block_idx].offset();
if (current_start > last_block_end + kCoalesceThreshold) {
if (current_start >
last_block_end + multiscan_opts->io_coalesce_threshold) {
// new IO
collapsed_blocks_to_read.emplace_back();
}
+5
View File
@@ -1847,6 +1847,10 @@ DEFINE_bool(universal_reduce_file_locking,
.compaction_options_universal.reduce_file_locking,
"See Options().compaction_options_universal.reduce_file_locking");
DEFINE_uint64(multiscan_coalesce_threshold,
ROCKSDB_NAMESPACE::MultiScanArgs().io_coalesce_threshold,
"Configures io coalescing threshold for multiscans");
namespace ROCKSDB_NAMESPACE {
namespace {
static Status CreateMemTableRepFactory(
@@ -6413,6 +6417,7 @@ class Benchmark {
while (!duration.Done(1)) {
DB* db = SelectDB(thread);
MultiScanArgs opts;
opts.io_coalesce_threshold = FLAGS_multiscan_coalesce_threshold;
std::vector<std::unique_ptr<const char[]>> guards;
opts.reserve(multiscan_size);
// We create 1 random start, and then multiscan will start from that
@@ -0,0 +1,2 @@
* Introduce `MultiScanArgs::io_coalesce_threshold` to allow a configurable IO coalescing threshold.