-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Micro optimizations for serialization #39323
Conversation
@@ -12,25 +12,24 @@ public sealed partial class Utf8JsonWriter | |||
{ | |||
private void ValidateWritingValue() |
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 not make this an aggressive-inlining method whose implementation is if (!skip) { ValidateWritingValueSlowNonInlined(); }
? Then you don't have to change a dozen call sites.
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.
@GrabYourPitchforks Mono interpreter currently can't inline methods with branches, so it would no longer provide any benefit there.
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.
It does not feel right to be doing manual inlining and making the code less maintainable just to make it run faster under interpreter.
The interpreter is always going to be a low throughput environment.
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.
Avoiding the overhead of calling a no-op method is a normal idiom.
Also the existing writer code already had the same up-front if
check before calling ValidateStart()
and ValidateEnd()
(instead of within the called method) so one way to think about this is extending that existing pattern to ValidateWritingValue
. Granted there were only 4 cases of that before and now there's 21.
While this looks good, I would prefer to wait for #39195, which should get in today. That change should fix most of the overhead with accessing the AllowedList property. I would expect the inlining of NeedsEscaping as that part of this PR to still have some additional benefit, but it's hard to be sure without testing it. |
I was hoping to get this in before the Preview 8 snap today. However it makes sense to wait and see if there is still a gain by caching the reference to the static |
@steveharter The interpreter PR was just merged. I think we could consider the more controversial inlining of NeedsEscape separately in another PR and have the rest of this merged in time for the snap. |
Test failure in dotnet-runtime-perf unrelated: |
Some runtime CI legs seem to be stuck (4.5 hours). |
Primarily this helps with Blazor performance on the Mono interpreter during serialization.
The writer tests are ~1.4x faster and serialization (which uses the writer) are ~1.25x faster. This is an existing benchmark we have been using that represents a large object graph. These timings taken from Edge browser:
The main perf benefit is due to avoiding accessing the
static ReadOnlySpan<byte>
table when checking to see if we need to escaper each characters. Instead, a local stack variable is made once. This performance may be able to be fixed in the interpreter @BrzVlad; not sure why this is much slower on Mono interpreter, although it could be taking a lock to ensure the static table is properly initialized.The non-Mono (core) numbers are a bit faster as well (about 2-3%) due to micro optimizations in the writer and serializer code and was not affected by the escaping change mentioned above. Here's a generic writer test: