-
Notifications
You must be signed in to change notification settings - Fork 10.2k
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
.net 6 aspnet minimal api not using the System.Text.Json source generator? #45064
Comments
Getters and setters are used per request. I don't see the serialize handler being used though. var opt = new JsonSerializerOptions();
opt.AddContext<MyJsonDtoContext>();
JsonSerializer.Serialize(new MyJsonDto(), opt); |
That example should result in the fast-path method being exercised, unless of course fast-path has been explicitly disabled in the source generator.
You've set breakpoints on the initialization method rather than the getter/setter delegates. It is expected that this should only be called once. |
@eiriktsarpalis can you reply to this #45064 (comment) |
OK I investigated this, and it comes down to the fact that this part of the JSON source generator (the serialization handlers) doesn't work with encoders. The Here's the full repro: using System;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
var opt = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
opt.AddContext<MyJsonDtoContext>();
Console.WriteLine(JsonSerializer.Serialize(new MyJsonDto(), opt));
public class MyJsonDto
{
public int Id { get; set; }
public string Name { get; set; }
}
[JsonSerializable(typeof(MyJsonDto))]
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
public partial class MyJsonDtoContext : JsonSerializerContext { } |
Cf. first section of my previous response.
This is by design. Because fast-path serialization essentially inlines all serialization logic, configuration needs to be specified at compile time (a subset of |
I tried placing breakpoints on all *.g.cs files (methods and getters/setters). Only the initialization is executed on the first request. Subsequently, the code does not go into *.g.cs files. I compared performance, load testing, web api without json source-generators and with it. Then I went to look in Debug mode. And realized that the code from the generators is not involved in processing HTTP requests. |
On each subsequent request you should be able to hit breakpoints either inside the getter/setter delegates or one of the
In most cases the source generator does not improve serialization performance. There should only be a few gains (~40%) if fast-path serialization is being used. |
~40% - it's huge gains, i think. I'm comparing manual serialization, where the real 40% performance in benchmarks. I expected to see similar numbers in webapi projects, api http requests, or at least 10%. And what does fast-path serialization mean? Is it some special serialization method available only at runtime? |
It's one of the two modes available to the source generator, as exposed in the
By default, the source generator will emit code for both modes and will only exercise the fast path if supported by the current |
It's even worse than that, from what I can tell. ASP.NET calls the aspnetcore/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs Lines 143 to 151 in ce78832
From inspecting the code in System.Text.Json, this API will never call into the "fast path" / This is because the And Repro: using System;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
//Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
options.AddContext<MyJsonDtoContext>();
Console.WriteLine(JsonSerializer.Serialize(new MyJsonDto(), options)); // calls the "fast path"
Stream body = Console.OpenStandardOutput();
await JsonSerializer.SerializeAsync(body, new MyJsonDto(), options, cancellationToken: default); // doesn't call the "fast path"
public class MyJsonDto
{
public int Id { get; set; }
public string Name { get; set; }
}
[JsonSerializable(typeof(MyJsonDto))]
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
public partial class MyJsonDtoContext : JsonSerializerContext { } |
Related: dotnet/runtime#75139 (comment)
Also note that the "fast path" logic doesn't get invoked when doing "sync" call passing in a |
It turns out that in GET requests, where only Serialization, source generators do not participate in optimization and do not speed up HTTP requests. So json source generators optimize only manual serialization, there is no point in adding them to WebApi via AddContext<>. Did i get it right? |
Triage: Closing as there is a PR to fix this in the runtime repo. |
Reference dotnet/runtime#78646 (review) |
Is there an existing issue for this?
Describe the bug
.NET 6
aspnet web project with minimal api
When using the .NET 6.0 System.Text.Json source generator with minimal api, it seems that source generators files (*.g.cs) never used on runtime, on HTTP request-response jobs.
I put a lot of stopping points in the source generators files (*.g.cs), but code execution does not go there.
Expected Behavior
I'm testing the performance of minimal api against System.Text.Json without source generators and with it in the context of web api projects.
I get exactly the same query results per second (RPS) on a simple POST query example. (on running Release mode of course)
I don't understand how to make the code generated through JsonSerializerContext run.
Calling
var json = JsonSerializer.Serialize(new MyJsonDto() { }, MyJsonDtoContext.Default.MyJsonDto);
manually, in console app, successfully enters the *.g.cs files on Debug mode. And the benchmark shows an increase in performance in Release mode.
Steps To Reproduce
https://github.com/NYMEZIDE/JsonSourceGeneratorsNotUsed/
Exceptions (if any)
No response
.NET Version
6.0.400
Anything else?
.NET SDK (reflecting any global.json):
Version: 6.0.400
Commit: 7771abd614
Среда выполнения:
OS Name: Windows
OS Version: 10.0.22000
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\6.0.400\
global.json file:
Not found
Host:
Version: 6.0.8
Architecture: x64
Commit: 55fb7ef977
.NET SDKs installed:
2.2.207 [C:\Program Files\dotnet\sdk]
6.0.400 [C:\Program Files\dotnet\sdk]
.NET runtimes installed:
Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Download .NET:
https://aka.ms/dotnet-download
Learn about .NET Runtimes and SDKs:
https://aka.ms/dotnet/runtimes-sdk-info
The text was updated successfully, but these errors were encountered: