-
Notifications
You must be signed in to change notification settings - Fork 5.5k
stats: extend the lock-free ref-count release fast path to histograms and relax ref-count orderings #46001
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stats: extend the lock-free ref-count release fast path to histograms and relax ref-count orderings #46001
Changes from 4 commits
1e07ee2
8e81390
c11c861
b560386
b63e81f
013ad46
2e82188
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| Extended the lock-free non-final reference-count release fast path (added for counters, | ||
| gauges and text readouts in a previous change) to histograms, which previously took the | ||
| store's histogram lock on every release, and switched stat reference-count operations to | ||
| standard reference-counting memory orderings instead of sequentially-consistent ones. | ||
| Processes with very large numbers of stats should see moderately lower flush times on top | ||
| of the earlier change (roughly a further 10-15% in the stats flush benchmark at millions of | ||
| stats). There are no visible behavioral changes. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1094,30 +1094,41 @@ ParentHistogramImpl::~ParentHistogramImpl() { | |
| hist_free(cumulative_histogram_); | ||
| } | ||
|
|
||
| void ParentHistogramImpl::incRefCount() { ++ref_count_; } | ||
| void ParentHistogramImpl::incRefCount() { ref_count_.fetch_add(1, std::memory_order_relaxed); } | ||
|
|
||
| bool ParentHistogramImpl::decRefCount() { | ||
| bool ret; | ||
| if (shutting_down_) { | ||
| // When shutting down, we cannot reference thread_local_store_, as | ||
| // histograms can outlive the store. So we decrement the ref-count without | ||
| // the stores' lock. We will not be removing the object from the store's | ||
| // histogram map in this scenario, as the set was cleared during shutdown, | ||
| // and will not be repopulated in histogramFromStatNameWithTags after | ||
| // initiating shutdown. | ||
| ret = --ref_count_ == 0; | ||
| } else { | ||
| // We delegate to the Store object to decrement the ref-count so it can hold | ||
| // the lock to the map. If we don't hold a lock, another thread may | ||
| // simultaneously try to allocate the same name'd histogram after we | ||
| // decrement it, and we'll wind up with a dtor/update race. To avoid this we | ||
| // must hold the lock until the histogram is removed from the map. | ||
| // | ||
| // See also StatsSharedImpl::decRefCount() in allocator.cc, which has | ||
| // the same issue. | ||
| ret = thread_local_store_.decHistogramRefCount(*this, ref_count_); | ||
| return ref_count_.fetch_sub(1, std::memory_order_acq_rel) == 1; | ||
| } | ||
|
|
||
| // Fast path: when this is not the last reference, drop it without taking | ||
| // the store's histogram lock; the CAS loop can only move the count between | ||
| // values >= 1, so it can never reach zero here. This mirrors the fast path | ||
| // in StatsSharedImpl::decRefCount() (see allocator.cc and #45821); the case | ||
| // analysis there applies unchanged, with the store's histogram lock playing | ||
| // the role of the allocator's mutex. | ||
| ASSERT(ref_count_ >= 1); | ||
| uint32_t ref_count_snapshot = ref_count_.load(std::memory_order_relaxed); | ||
| while (ref_count_snapshot > 1) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we share this block of code somehow, maybe via a helper class, with the counter/gauge mechanism in allocator.cc?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, |
||
| if (ref_count_.compare_exchange_weak(ref_count_snapshot, ref_count_snapshot - 1, | ||
| std::memory_order_acq_rel)) { | ||
| return false; | ||
| } | ||
| } | ||
| return ret; | ||
|
|
||
| // Slow path: we may hold the last reference, so we delegate to the Store | ||
| // object to decrement the ref-count under the lock to the map. If we don't | ||
| // hold a lock, another thread may simultaneously try to allocate the same | ||
| // name'd histogram after we decrement it, and we'll wind up with a | ||
| // dtor/update race. To avoid this we must hold the lock until the histogram | ||
| // is removed from the map. | ||
| return thread_local_store_.decHistogramRefCount(*this, ref_count_); | ||
| } | ||
|
|
||
| bool ThreadLocalStoreImpl::decHistogramRefCount(ParentHistogramImpl& hist, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you combine this with the previous changelog entry? From the reader/users point of view, it doesn't matter that it was two separate PRs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. #45821 didn't add a changelog entry, so I reworded this one to describe the combined change from the user's point of view (counters/gauges/text readouts and histograms together, no per-PR split).