-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Estimate StringBuilder capacity for exported names #919
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
base: main
Are you sure you want to change the base?
Estimate StringBuilder capacity for exported names #919
Conversation
When we generate metric names, we use the default `StringBuilder` constructor, which allocates a _16 character_ buffer. Most metrics will exceed this, especially when we start adding attributes to it. This causes `StringBuilder` to "grow", automatically, by allocating a new array and copying its contents over to it. Each time it only grows enough to contain the `append`ed String, which means we need to grow on almost every `append` call. While allocating new memory is cheap in the JVM, copying memory is not. These copies add up, especially with a large number of long-named metrics with many attributes. Instead, we can calculate the necessary capacity of the `StringBuilder` up-front, which should avoid doing any copying during `append`. Signed-off-by: Nick Telford <[email protected]>
I'm no longer convinced that the lack of estimating I think the primary cause of the flame graph above is actually simply poor optimizations of String concatenation in code compiled under Java 8. See #920 for more details. Since computing the exact size up-front requires iterating the attributes, it might not actually be much more efficient to do this. However, since copying memory is generally less efficient than iterating a linked list, I'm going to leave this PR open, as it will probably provide a very modest performance improvement. |
may be faster overall to just set an initial capacity on all the StringBuilder instances like 256 bytes - these are so short-lived that with escape analysis, they may be fully stack-allocated and never touch the heap. and yes @nicktelford a lot of this is from naive String concatenation as noted in #920 - i tried replacing all String concatenation with StringBuilder calls and got this reduced, however it was a bit messy. Best outcome was a shared StringBuilder, reset per-use via setLength(0). |
@DSmithVA are your code changes available? Thoughts on using a ThreadLocal StringBuilder?
We could then acquire/release it in the |
I don't have the StringBuilder changeset anymore, it was fairly noisy/intrusive for something that could've just been fixed by a newer java version. By far, a bigger improvement was to cache MBeans between runs and update the cache via NotificationListener (special case for DynamicMBean impls), but the changes kept getting more involved, to the point where a rewrite was a lot less work. |
When we generate metric names, we use the default
StringBuilder
constructor, which allocates a 16 character buffer.
Most metrics will exceed this, especially when we start adding
attributes to it.
This causes
StringBuilder
to "grow", automatically, by allocating anew array and copying its contents over to it. Each time it only grows
enough to contain the
append
ed String, which means we need to grow onalmost every
append
call.While allocating new memory is cheap in the JVM, copying memory is not.
These copies add up, especially with a large number of long-named
metrics with many attributes.
Instead, we can calculate the necessary capacity of the
StringBuilder
up-front, which should avoid doing any copying during
append
.Signed-off-by: Nick Telford [email protected]