-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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: symbolize strings in HeapStatData and ThreadLocalStore #4281
Conversation
Signed-off-by: James Buckland <[email protected]>
@mrice32 can you take an initial pass at this? Thanks |
Looks like |
d26d887
to
1cc4a41
Compare
Signed-off-by: James Buckland <[email protected]>
1cc4a41
to
a5bb73b
Compare
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.
Nice work! Some initial comments.
Signed-off-by: James Buckland <[email protected]>
include/envoy/stats/symbol_table.h
Outdated
}; | ||
|
||
using StatNamePtr = std::unique_ptr<StatName>; | ||
|
||
struct StatNameHash_ { |
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.
why the trailing underscore here? and StatNameCompare_ ?
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.
I must have thought it was idiomatic for some reason. Will change.
Signed-off-by: James Buckland <[email protected]>
5ad54a1
to
65ddd61
Compare
This has the benefit of avoiding a dynamic_cast inside operator==()s. Signed-off-by: James Buckland <[email protected]>
@ all, I'm continuing to profile this for both heap alloc. and time performance. This won't be ready to review (let alone merge) until I'm confident it doesn't trade memory for slowness. |
Signed-off-by: James Buckland <[email protected]>
Signed-off-by: James Buckland <[email protected]>
@@ -104,21 +104,21 @@ class SymbolTable { | |||
|
|||
// Stores the symbol to be used at next insertion. This should exist ahead of insertion time so | |||
// that if insertion succeeds, the value written is the correct one. | |||
Symbol next_symbol_ = 0; | |||
Symbol next_symbol_ = 0 GUARDED_BY(lock_); |
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.
Symbol next_symbol_(0) GUARDED_BY(lock_)
?
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.
I'm going to make this explicit in a constructor, easier than fighting a macro.
source/common/stats/heap_stat_data.h
Outdated
// the destructors of the individual stat objects, which are not protected by locks. | ||
// A locally held symbol table which encodes stat names as StatNamePtrs and decodes StatNamePtrs | ||
// back into strings. | ||
SymbolTable table_; |
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.
Add commentary here that table_ itself is not protected by mutex_ and guarantees thread safety on its own.
source/common/stats/heap_stat_data.h
Outdated
return (a->key() == b->key()); | ||
} | ||
struct HeapStatCompare { | ||
bool operator()(const HeapStatData* a, const HeapStatData* b) const { return (*a == *b); } |
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.
nit: don't need parens around *a == *b
struct StatNamePtrCompare { | ||
bool operator()(const StatName* a, const StatName* b) const { | ||
// This extracts the underlying statnames. | ||
return (*a == *b); |
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.
nit: extra parens
struct StatNameUniquePtrCompare { | ||
bool operator()(const StatNamePtr& a, const StatNamePtr& b) const { | ||
// This extracts the underlying statnames. | ||
return (*a == *b); |
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.
ditto
Following up on the question I've asked in #4190 - how exactly are you measuring memory usage and what's the concurrency setting you're using in your tests? The 70x jump in memory usage cannot be attributed only to the increase in number of clusters. |
Signed-off-by: James Buckland <[email protected]>
Signed-off-by: James Buckland <[email protected]>
@PiotrSikora I'm running |
@ambuc but how are you checking memory usage? Using RSS value from the As for the concurrency, you can check it via |
Per Piotr's comment, can you edit the description in this PR to include a pointer to the "how to run memory profiling" thing that you wrote, right above or below the table? |
Also you pinged me a table showing the CPU consumption impact of this PR. Can you add that to the description as well? |
@PiotrSikora Ah, I see. I'm not doing either of those -- I was compiling with tcmalloc (as in https://github.com/envoyproxy/envoy/blob/master/bazel/PPROF.md) and using the high-water mark pprof spit out. I'll run this again with |
@PiotrSikora I'm not convinced that using @ all, is this a "bug"? Should I take out an issue to fix this behavior? It would be nice for a memory monitoring feature to not significantly change the memory on which it reports. |
@ambuc yeah, I'm aware of this (and I had a patch that emits memory stats to logs upon receiving |
I suspect that in the context of this benchmark which generates large numbers of long-named clusters, the overhead of generating all /stats is likely to be significant, as it makes a map of fully elaborated strings for all stat names, thus defeating the purpose of storing them as a vector of shared symbol references in steady-state. I think if we add a query-param to /stats to just emit a single stat without elaborating all of them into a map, the results would more accurately reflect the value of what James has done. |
@jmarantz @PiotrSikora I have a PR here #4361 which lets you inspect (Also, all these trials are run with concurrency 12.) |
This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions! |
Per discussion in #4196, I am going to wait on this change, which might lock us into a stat implementation unnecessarily. Closing for now. |
Signed-off-by: Joshua Marantz <[email protected]>
Description: On the heels of #3927, and in pursuit of lower heap consumption for Envoy (#3585), this implementation uses the new
Stats::SymbolTable
library to symbolizestd::string
instances withinHeapStatData
andThreadLocalStore
.Preliminary heap allocation improvements here:
NB: For these results, build with
-c opt
and then query/stats
forserver.memory_heap_size
. A PR with this query which does not necessitate the generation of all stats is here: #4361. All runs are againstenvoy-static
with--disable-hot-restart
. "short" cluster names are ~10 chrs long, and "long" cluster names are ~60 chrs long.Risk Level: Medium -- this affects non-hot-restart binaries, so there won't be any issues with trying to restart against a different internal memory representation. But any PR which changes internal memory stuff comes with some risk.
Testing: As a drop-in replacement for HeapStatData / ThreadLocalStore, there are no new tests for these classes.
Docs Changes: N/A
Release Notes: N/A, no user-facing changes.
Fixes #3585