Conversation
…d update buffers Reservoir updates accumulate in a thread-local buffer instead of going to the shared striped arrays one at a time. An update encodes into a single int -- reservoir id, bucket index, and a delta from the buffer's baseline second -- so the buffer is a plain int[], shared by every reservoir the thread updates, with a single writer and multiple readers. A flush counts identical entries through an open addressing hashtable, which collapses repeats into one atomic update per bucket. Many histograms record a near-constant value (written row counts, for instance) and latencies are typically multi-modal, so that aggregation removes most of the atomic traffic. A flush also groups one reservoir's entries together, so each reservoir is applied in a single call that reuses the exponential decay factors it computed and touches the reservoir's arrays with better locality. Sorting proved too slow for this, so grouping uses a second open addressing hashtable keyed by reservoir id: it counts the entries per reservoir, turns those counts into ranges, and compacts each entry into its reservoir's range. Every structure a flush uses is reused, so a flush allocates nothing. Metric reads flush the buffers first. A scrape may read thousands of histograms in a row, so a time check for the last flush is introduced to reduce flush rate Additionally: - findIndex reads a short[] lookup table instead of computing log2 in floating point - the bucket arrays carry a leading cache line of padding, so no live bucket shares a line with the array length field that every bounds check reads - MonotonicClock.nowInSec() is sampled by the approximate clocks, so the decay path no longer divides nanoseconds on every update NOTE: snapshot merging rescaled by a wrong landmark difference divided patch by Dmitry Konstantinov; reviewed by TBD for CASSANDRA-20333
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…
Reservoir updates accumulate in a thread-local buffer instead of going to the shared striped arrays one at a time. An update encodes into a single int -- reservoir id, bucket index, and a delta from the buffer's baseline second -- so the buffer is a plain int[], shared by every reservoir the thread updates, with a single writer and multiple readers.
A flush counts identical entries through an open addressing hashtable, which collapses repeats into one atomic update per bucket. Many histograms record a near-constant value (written row counts, for instance) and latencies are typically multi-modal, so that aggregation removes most of the atomic traffic.
A flush also groups one reservoir's entries together, so each reservoir is applied in a single call that reuses the exponential decay factors it computed and touches the reservoir's arrays with better locality. Sorting proved too slow for this, so grouping uses a second open addressing hashtable keyed by reservoir id: it counts the entries per reservoir, turns those counts into ranges, and compacts each entry into its reservoir's range. Every structure a flush uses is reused, so a flush allocates nothing.
Metric reads flush the buffers first. A scrape may read thousands of histograms in a row, so a time check for the last flush is introduced to reduce flush rate
Additionally:
NOTE: snapshot merging rescaled by a wrong landmark difference divided
patch by Dmitry Konstantinov; reviewed by TBD for CASSANDRA-20333