-
Notifications
You must be signed in to change notification settings - Fork 217
Reduce allocations under SyntaxNodeExtensions.GetContent #11946
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
Reduce allocations under SyntaxNodeExtensions.GetContent #11946
Conversation
This method was creating a new StringWriter on every invocation. On net framework, each StringWriter incurs an additional char array allocation during construction. Instead, add a pool for stringwriters in GreenNode to avoid these allocations on every call. This reduced allocations spent under this method during the typing scenario in the razor lsp speedometer test from 3.5% to 1.9%. Note that it would be nice to have GreenNode.ToString use this optimization too, however it differed (intentionally?) from the caller in SyntaxNodeExtensions.GetContent by passing in the invariant culture to the StringWriter during construction. I don't see ToString called near as often in the profile I'm looking at, so, it didn't seem to warrant creating a separate pool for that method. I would appreciate verification though that it's intentional that these two calls use different culture semantics. Speedometer run: https://dev.azure.com/devdiv/DevDiv/_apps/hub/ms-vseng.pit-vsengPerf.pit-hub?targetBuild=10711.132.dn-bot.250612.052736.643013&targetBranch=main&targetPerfBuildId=11746547&runGroup=Speedometer&baselineBuild=10711.132&baselineBranch=main
| public static string GetContent<TNode>(this TNode node) where TNode : SyntaxNode | ||
| { | ||
| using var _ = StringBuilderPool.GetPooledObject(out var builder); | ||
| using var writer = new System.IO.StringWriter(builder); |
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.
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 really don't think you need to worry about CultureInfo for the StringWriter. GreenNode.WriteTo delegates to GreenNode.WriteTokenTo to do any actually writing. WriteTokenTo is only overridden by SyntaxToken and it just writes a string. IOW, nothing actually writes anything other than strings, so it's essentially culture-neutral.
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.
That's what I was hoping. Went ahead and changed to always use InvariantCulture. Thanks!
| return builder.ToString(); | ||
| } | ||
|
|
||
| internal string ToStringInCurrentCulture() |
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.
Right above here is where ToString instead specifies InvariantCulture.
If these were to match, the ToStringInCurrentCulture change could be moved to ToString.
This method was creating a new StringWriter on every invocation. On net framework, each StringWriter incurs an additional char array allocation during construction. Instead, add a pool for stringwriters in GreenNode to avoid these allocations on every call. This reduced allocations spent under this method during the typing scenario in the razor lsp speedometer test from 3.5% to 1.9%.
Note that it would be nice to have GreenNode.ToString use this optimization too, however it differed (intentionally?) from the caller in SyntaxNodeExtensions.GetContent by passing in the invariant culture to the StringWriter during construction. I don't see ToString called near as often in the profile I'm looking at, so, it didn't seem to warrant creating a separate pool for that method. I would appreciate verification though that it's intentional that these two calls use different culture semantics.
Speedometer run: https://dev.azure.com/devdiv/DevDiv/_apps/hub/ms-vseng.pit-vsengPerf.pit-hub?targetBuild=10711.132.dn-bot.250612.052736.643013&targetBranch=main&targetPerfBuildId=11746547&runGroup=Speedometer&baselineBuild=10711.132&baselineBranch=main
Allocations before change

Allocations with change
