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
Improve ReuseableStringBuilderFactory #2697
Comments
There is already some diagnostic logging in there in debug -- improving that and running some scenarios would be a great way to tune it to first order. Analogous to how the OpportunisticIntern class can dump stats to help adjust its thresholds. So much easier than iterating with a profiler right away. In particular it would be interesting to log how large (both length and capacity) the builders are when they are returned (which indicates how large they needed to be, and how often they are discarded). My guess is that they are often getting discarded as too large (>1024 chars). If the threshold was higher, the default size wouldn't matter much as they would rarely be recreated. It could even be tuned, ie., if there's a lot of discards, then increase the size you accept; if not many, then there's likely less value in keeping it around. |
Labeling with size:1 as the cost of the initial investigation. Will open a follow-up issue if more work is identified. |
Would it help if StringBuilder could get (and return) its backing arrays through an array pool? This would just move the caching down of course but it could be tuned to avoid any that go on LOH. It might be easier to reason about the caching strategy. |
Having SB which is backed up by array pool would be ideal, IMHO. I asked Stephen that question about half a year ago. msbuild/src/StringTools/SpanBasedStringBuilder.cs Lines 9 to 18 in a59d7a5
As this is even better, in certain uses cases, than theoretical ArrayPoolBackedStringBuilder . SpanBasedStringBuilder has one less arraycopy than ArrayPoolBackedStringBuilder which has to arraycopy from source to buffer and then from buffer to final string char[len] while SpanBasedStringBuilder just arraycopy from spans to final string char[len] . It might make difference with some huge strings. SpanBasedStringBuilder have some missing API (AppendFormat , Append(char) , ...) to be used in some niche use cases though.
|
cc @stephentoub in case he is interested in this discussion. I am mainly interested in case anything is learned that would be of general interest in the core libraries. (I doubt SpanBasedStringBuilder would be)
Never mind that part. I remembered that SB already has a max chunk size of 8KB, unless you initialize it with something smaller. So it would be necessary to establish that backing with an ArrayPool actually is helpful or is merely moving the caching around. |
While I was editing in this area I noticed that the calling pattern around StringBuilders in FormatEventMessage looked allocatey. Instead of creating two throwaway StringBuilders to format a single message, 1. Grab a ReusableStringBuilder 2. Reuse the builder between "construct format string" and "get final message". We chose ReusableStringBuilder over StringBuilderCache because logging sometimes creates strings that are _much_ larger than the 512 character limit of SBC. That also reduces the need to prereserve a size: the process-wide pool's elements should be pretty big already. See dotnet#2697 (comment) for stats on string length.
There's a bunch of improvements that I've noticed will help a lot:
The text was updated successfully, but these errors were encountered: