-
Notifications
You must be signed in to change notification settings - Fork 550
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
Propose promextra.CounterVec
/ promextra.GaugeVec
to reduce allocations
#6193
Conversation
Each time we call WithLabelValues(...) we have to allocate a slice for that single argument that is immediately thrown away. I propose to replace that in our hot paths by a wrapper that would reuse that slice through a pool. Signed-off-by: Oleg Zaytsev <mail@olegzaytsev.com>
promextra.CounterVec
/ promextra.GaugeVec
to reduce allocations
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 not against this change but I think the canonical way to solve this problem is getting the WithLabelValues()
reference once and then just use it over the time: have you considered just grabbing the per-tenant metrics when creating userTSDB
and storing the references there?
I don't know how much memory may increase, but let's say we have 500 tenants / ingester, 50 metrics per tenant, and each metric is 1KB (which I guess it's an overestimation): it's +25MB / ingester, which I guess won't be even noticeable.
Right, I guess I chose a bad example (I took a random file) for this. You're right, we should do that for userTSDB metrics where possible, as we could also make the lookup only once. However, there are many places where this can't be applied. |
Does it really help, or are you fighting the battle that compiler already optimizes? Where are the benchmarks? |
I am, unless it's provably and considerably faster. I don't think we need to make our codebase more complicated for miniscule improvements. |
Signed-off-by: Oleg Zaytsev <mail@olegzaytsev.com>
My opinion is that this change isn't more complicated as we're just wrapping with something that is more performant, but used in a similar way. This can't be optimized by the compiler (at least at this point) as there's no way to know where that slice ends up. Anyway, I hopelessly ran (because in the huge amount of work that Ingester.Push does, it's hard to find the impact of 1-2 less allocations) and I was surprised by the results. I even had to run it 50 times to make sure the result is consistent:
I also ran
|
I took a quick look at doing this:
|
@pstibrany pointed out that this should be optimized by the compiler, and @bboreham pointed to how it can be fixed, so this is superseded by the fix in prometheus/client_golang#1360 |
That's quite interesting fix, very nice work! |
I tried benchmarking that fix vendored to Mimir and 🚀 👍
|
What this PR does
Each time we call WithLabelValues(...) we have to allocate a slice for that single argument that is immediately thrown away.
These allocations can't be seen on the profiling because it's the caller, not the
WithLabelValues
who performs an allocation, so they're spread across the entire codebase.I propose to replace that in our hot paths by a wrapper that would reuse that slice through a pool. I replaced the usages in
pkg/ingester/metrics.go
as an example. Other usages would be done in follow up PRs. Most of our usages only need to pass one or two labels, so I did just that.I didn't embed
prometheus.CounterVec
on purpose: this way it's easier to refactor without missing calls unchanged. I'm also leaving that field exported on purpose, as some usages may still need to access other methods.I didn't want to spend too much time on naming, I'm open for discussion on that
promvec.Counter
could be another alternative.Which issue(s) this PR fixes or relates to
None, just passing by.
Checklist
CHANGELOG.md
updated - the order of entries should be[CHANGE]
,[FEATURE]
,[ENHANCEMENT]
,[BUGFIX]