-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
JsonSerializerOptions.MemberAccessorStrategy shouldn't use Reflection.Emit when IsDynamicCodeCompiled is false #38693
Comments
FWIW we previously used But using an ifdef branch for netcoreapp that checks RuntimeFeature should work great. |
I ran the attached JSON perf application with and without this change. 5.0.0-preview.8.20354.5
Proposed change
So it is ~15-20% slower to use the Reflection based strategy after everything is warmed up. (Note this is explicitly not testing the time it takes to emit IL during warmup.) |
Since the performance tradeoff of taking this change isn't necessarily a net-win, we won't be making this change in 5.0. Moving to 6.0. |
With the advent of the JSON source generator, we can solve the ~15-20% slower to use Reflection based strategy issue above by telling places that need fast JSON Serialization to use the source generator. With that in mind, I created a prototype of introducing a feature switch in System.Text.Json to use the This allows for the following size savings (.br compressed): Before: 2,644,974 bytes So roughly 16.5KB .br compressed size savings. This could be an alternative approach to allow for the Ref.Emit code to be trimmed in a default Blazor WASM app, even if not all the places that use JSON Serialization use the source generator. |
Now that the Using the rough numbers of summing all the *.br files in the publish directory, here are the results I am seeing:
So paired with the re-linking feature, if we can trim away the Reflection.Emit code, it would trim 25 KB compressed. And if you aren’t re-linking we’d save the estimated ~18 KBs, as above. However, it still comes at a tradeoff for throughput. I re-ran the above Ref.Emit + re-linking:
Trim Ref.Emit + re-linking:
As you can see, the performance of Json serialization has gotten drastically better from my original numbers (I may have been on a different machine as well). However, the difference between using Ref.Emit vs. Reflection has gotten wider. For "serialize" - ~48% worse, and for deserialize - ~73% worse. For ahead-of-time (AOT) compilation, here are the differences: Size difference, the only file that changes when using AOT is the
So a ~62 KB .br compressed size savings. The throughput ratio between Ref.Emit vs Reflection is about the same on AOT as it is on non-AOT: Ref.Emit + AOT:
Trim Ref.Emit + AOT:
|
@radekdoulik @BrzVlad could you work with Eric to investigate why no ref-emit AOT case is noticeably slower and if the code is fully AOTed or not. |
I tried to replicate it locally with browser-bench sample and I see similar results (interp/amd64/chrome):
|
I think that still does not explain what is causing AOT SRE free version to be about 50% slower than the SRE version which is mostly interpreted. |
Indeed, I started looking into that and wanted to share that I can replicate it too, with different simple app without Blazer involved. |
Moving to Future. I don't think this work can happen until the runtime performance impact is decreased. With the current numbers, the runtime performance impact is too great for the small size reduction. |
Appears to have been fixed by #54027. |
There are 2 different switches here:
#54027 allows JsonSerializer work at all when you can't IL Emit code. This issue is to track the 2nd case, when you can IL Emit code, but it get interpreted. |
When trimming a Blazor WASM app, the last usage of Reflection.Emit (after fixing #38678) is coming from System.Text.Json.Serialization.JsonSerializerOptions:
runtime/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs
Lines 423 to 438 in d89772a
However, on Mono WASM,
RuntimeFeature.IsDynamicCodeCompiled
is always false, so using Reflection.Emit is probably a waste, and it brings in a decent amount of code. In my investigations I find it removing ~50KB of IL if we trim this usage of Reflection.Emit.We should change this code to something more like:
With changing the code to the above, on a default template Blazor WASM app, I am seeing size savings of:
So almost a 50 KB savings by allowing the removing all usages of System.Reflection.Emit.
cc @steveharter @layomia @vitek-karas @marek-safar
The text was updated successfully, but these errors were encountered: