Add new heauristic 'num_collapsible_entry_reads_sampled' #14434
Add new heauristic 'num_collapsible_entry_reads_sampled' #14434joshkang97 wants to merge 4 commits into
Conversation
✅ clang-tidy: No findings on changed linesCompleted in 291.3s. |
|
@joshkang97 has imported this pull request. If you are a Meta employee, you can view this in D95613793. |
|
@joshkang97 has imported this pull request. If you are a Meta employee, you can view this in D95613793. |
xingbowang
left a comment
There was a problem hiding this comment.
Looks good overall.
I don't see num_collapsible_entry_reads_sampled is used. I guess there is a follow up change to leverage it in the compaction logic.
| // Decrease probability of sampling next() to discount it as it is cheaper | ||
| // than seek() |
There was a problem hiding this comment.
To be more accurate, it is more like next() is called a lot more frequent than seek. Therefore, we want to lower the sampling rate to avoid introducing performance penalty. I guess this is why we use a counter instead of random to make sampling decision as well.
There was a problem hiding this comment.
The actual idea is to decrease probability but keep the amount the same. The reasoning is that the read cost of a seek() is ~64x (very loose estimation) as expensive as a next().
And yep the lowered probability lowers the cost of sample_collapsible_entry_file_read_inc(), and using a counter is more performant than division
| inline void sample_file_read_inc(const FileMetaData* meta) { | ||
| meta->stats.num_reads_sampled.fetch_add(kFileReadSampleRate, | ||
| std::memory_order_relaxed); | ||
| } | ||
|
|
||
| inline void sample_collapsible_entry_file_read_inc(const FileMetaData* meta) { | ||
| meta->stats.num_collapsible_entry_reads_sampled.fetch_add( | ||
| kFileReadSampleRate, std::memory_order_relaxed); | ||
| } |
There was a problem hiding this comment.
Should we increase different amount based on whether it is seek or next, as their sampling rate is different.
There was a problem hiding this comment.
Replied in my other comment
|
@joshkang97 merged this pull request in 42eff8b. |
) Summary: Add per-file sampling of "collapsible" entry reads (single deletions, merges, and kNotFound results) that may later be used to help inform read-triggered compactions. This is a better metric than `num_reads_sampled` as it is more targeted towards reads that could be avoided via compaction. The existing behavior of `num_reads_sampled` is that reads only gets sampled on iterator creation for a file. It is problematic because next/prev() calls are not sampled, nor are additional seeks(). This PR moves sampling to per-seek/next granularity within `LevelIterator` and adds a new `num_collapsible_entry_reads_sampled` counter that tracks how often a file serves entries that could be eliminated by compaction. Note only L1+ files have iterator seeks/nexts/prevs sampled. Introducing this at L0 would require wrapping table reader iterators, introducing a performance cost. ## Key changes - **New counter `num_collapsible_entry_reads_sampled`** in `FileSampledStats` tracks sampled reads that encounter deletions, single deletions, merges, or kNotFound results in both Get and Iterator paths. - **Moved sampling from file-open to per-operation** in `LevelIterator`: sampling now happens in `SampleRead()` called from `Seek()`, `SeekForPrev()`, `SeekToFirst()`, `SeekToLast()`, `Next()`, `NextAndGetResult()`, and `Prev()`. The `should_sample` parameter was removed from `LevelIterator`'s constructor. - **Differentiated sampling rate for Next() vs Seek()**: `should_sample_file_read_next()` uses a 64x lower sampling rate (`kFileReadSampleRate * 64`) since Next() is cheaper than Seek() and called more frequently. - **Collapsible tracking in Get path**: `Version::Get()` now increments the collapsible counter when `GetContext::State()` is `kNotFound`, `kMerge`, or `kDeleted`. - **Collapsible tracking in MultiGet path**: `MultiGetFromSST` also increments the collapsible counter for the same states. Pull Request resolved: facebook#14434 Test Plan: - Added new DB tests for both num_reads_sampled and num_collapsible_entry_reads_sampled ### Benchmark results (readrandom, readseq) Setup: 1M keys, 16-byte keys, 100-byte values, no compression, fillrandom+compact | Benchmark | Params | ops/s (main) | ops/s (feature) | % change | |------------|--------------------|-------------|--------------------------|----------| | readrandom | seed=1, threads=1 | 387,194 | 389,449 | +0.6% | | readseq | seed=1, threads=1 | 5,598,371 | 5,572,975 | -0.5% | No meaningful performance regression observed — differences are within run-to-run noise. Reviewed By: xingbowang Differential Revision: D95613793 Pulled By: joshkang97 fbshipit-source-id: 9dd09c9b7527b148424bde5686f4157c7a9e1214
Summary
Add per-file sampling of "collapsible" entry reads (single deletions, merges, and kNotFound results) that may later be used to help inform read-triggered compactions. This is a better metric than
num_reads_sampledas it is more targeted towards reads that could be avoided via compaction.The existing behavior of
num_reads_sampledis that reads only gets sampled on iterator creation for a file. It is problematic because next/prev() calls are not sampled, nor are additional seeks().This PR moves sampling to per-seek/next granularity within
LevelIteratorand adds a newnum_collapsible_entry_reads_sampledcounter that tracks how often a file serves entries that could be eliminated by compaction.Note only L1+ files have iterator seeks/nexts/prevs sampled. Introducing this at L0 would require wrapping table reader iterators, introducing a performance cost.
Key changes
num_collapsible_entry_reads_sampledinFileSampledStatstracks sampled reads that encounter deletions, single deletions, merges, or kNotFound results in both Get and Iterator paths.LevelIterator: sampling now happens inSampleRead()called fromSeek(),SeekForPrev(),SeekToFirst(),SeekToLast(),Next(),NextAndGetResult(), andPrev(). Theshould_sampleparameter was removed fromLevelIterator's constructor.should_sample_file_read_next()uses a 64x lower sampling rate (kFileReadSampleRate * 64) since Next() is cheaper than Seek() and called more frequently.Version::Get()now increments the collapsible counter whenGetContext::State()iskNotFound,kMerge, orkDeleted.MultiGetFromSSTalso increments the collapsible counter for the same states.Test Plan
Benchmark results (readrandom, readseq)
Setup: 1M keys, 16-byte keys, 100-byte values, no compression, fillrandom+compact
No meaningful performance regression observed — differences are within run-to-run noise.