Skip to content
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

Use .net 6 json source generators for performance #625

Open
gfs opened this issue Dec 21, 2021 · 12 comments
Open

Use .net 6 json source generators for performance #625

gfs opened this issue Dec 21, 2021 · 12 comments
Labels
enhancement New feature or request

Comments

@gfs
Copy link
Contributor

gfs commented Dec 21, 2021

https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/

Benchmarks show 30-40% faster serialization performance.

@gfs gfs added the enhancement New feature or request label Dec 21, 2021
@WingZer0o
Copy link

WingZer0o commented May 27, 2024

Would this require a version bump of .NET 5 to .NET 6? Are their any specific areas that need this done?

@gfs
Copy link
Contributor Author

gfs commented May 28, 2024

The main branch is actually behind. On Release 2.3 net 6 is the lowest version so this should be doable now. I think it mostly requires jut adding annotations to the correct objects - there's two different ways that the objects get serialized - either for JSON format output (though sarif is recommended and I don't think this would help in that respect as the sarif SDK uses newtonsoft rather than System.Text.JSON) and for the internal serialization used to store collected data in the sqlite database.

For regular json serialization it looks to me like we are using Newtonsoft so the method would have to be converted to System.Text.Json here:

private static ASA_ERROR ExportCompareResults(ConcurrentDictionary<(RESULT_TYPE, CHANGE_TYPE), List<CompareResult>> resultsIn, ExportOptions opts, string baseFileName, string analysesHash, IEnumerable<AsaRule> rules)
as well as annotating the appropriate objects.

For the second aspect I think the things that need to be annotated are the things that inherit from Collect/Compare objects. I think this also requires updating the JsonUtils helper to use System.Text.Json instead of Newtonsoft to actually take advantage of the perf uplift.

public abstract class CollectObject

public static CollectObject? Hydrate(string serialized, RESULT_TYPE type)

@WingZer0o
Copy link

Something that I discovered that is necessary to complete this in a semi similar fashion with the Dehydration of CollectObjects is https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism?pivots=dotnet-8-0 this feature set is not supported in .NET 6 and I had to remove the .NET 6 support to test out the feature.

After removing the .NET 6 support I am receiving this error even on the derived class of CertificateObject.

System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'Microsoft.CST.AttackSurfaceAnalyzer.Objects.SerializableCertificate'. Path: $.Certificate | LineNumber: 0 | BytePositionInLine: 31. ---> System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'Microsoft.CST.AttackSurfaceAnalyzer.Objects.SerializableCertificate'.

Is this still something you want to proceed with?

@gfs
Copy link
Contributor Author

gfs commented May 30, 2024

For the first issue, it should be fine to drop net 6, it ends support later this year anyway. Switching to NET 8 only I think would be fine.

For the second issue, I think the issue may be both Newtonsoft and System.Text.Json use [JsonConstructor] as the name of the attribute to specify a specific parameterized constructor for serialization (From this table it looks like they used the same name for the tag: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-9-0#specify-constructor-to-use-when-deserializing) - I hypothesize it may be the case that SerializedCertificate is importing newtonsoft rather than system.text.json, so when it gets to try to serialize the SerializableCertificateObject it won't recognize the attribute and thus can't determine what constructor to us?

@WingZer0o
Copy link

I can have a look into this pretty soon maybe tonight or this weekend. Let me double check the namespace.

@WingZer0o
Copy link

WingZer0o commented May 31, 2024

So I annotated and replaced all of the serialization and deserialization in JsonUtil and got the hydration test suite to pass with green lights.

I will slowly pick away at the rest of the application.

Do we want to completely uninstall Newtonsoft as a dependency since this switch over to System.Text.Json is happening?

Also what about some of these JsonSeralization/Deseralization methods that are taking options? I am importing the System.Text.Json one and it seems like lots of these options don't exist.

@WingZer0o
Copy link

WingZer0o commented May 31, 2024 via email

@gfs
Copy link
Contributor Author

gfs commented May 31, 2024

Do we want to completely uninstall Newtonsoft as a dependency since this switch over to System.Text.Json is happening?

I think the goal ideally is to migrate all the direct ASA code to use System.Text.Json, though, it can't be eliminated as a transitive dependency because its required by the Sarif SDK.

Also what about some of these JsonSeralization/Deseralization methods that are taking options? I am importing the System.Text.Json one and it seems like lots of these options don't exist.

Do you mean like the Should Serialize methods like this one?

public bool ShouldSerializeDiffs()

There is an alternate way to have custom logic but only on NET 7+. https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/custom-contracts

I think we do want to keep the behavior that empty fields aren't serialized if possible, it saves a fair few bytes, it looks like for .NET 6 it is possible to not serialize default values, which is likely the first thing to try. If there's other places in particular you're concerned about not having an equivalent option and you can point them out I may be above to advise more explicitly how I'd prefer to handle them.

To continue supporting .NET 6 there is a way to serialize derived
classes that should work across 6, 7, and 8 that might be a good idea to
proceed with.

That sounds great, if it can work across all the currently supported frameworks that's I think simpler to merge, but its not I think a hard requirement as there's only a couple months of support left for .NET 6/7 anyway.

@WingZer0o
Copy link

I was mostly referring to the Serializer settings that are accpected as a parameter to the JsonSerializer for Newtonsoft like the following in WriteMonitorJson.

JsonSerializer serializer = JsonSerializer.Create(new JsonSerializerSettings()
{
    Formatting = Formatting.Indented,
    NullValueHandling = NullValueHandling.Ignore,
    DefaultValueHandling = DefaultValueHandling.Ignore,
    Converters = new List<JsonConverter>() { new StringEnumConverter() }
});

@gfs
Copy link
Contributor Author

gfs commented Jun 5, 2024

I think it would be good to preserve similar functionality with system text.json ahen possible. I think there are equivalent settings for each of those in the link I sent above for converting from newtonsoft to system.text..

@WingZer0o
Copy link

Perhaps I'm a little out of depth with that task at hand to convert these over, but I am not seeing the same name properties within JsonSerializerOptions as JsonSerializerSettings.

image

@gfs
Copy link
Contributor Author

gfs commented Jun 7, 2024

@WingZer0o That's okay then, I can handle putting those back when you put up a PR to review if you want to skip it then. Finding the equivalent for each is a bit of a scavenger hunt and they're not always 1:1, but this page has the mappings for most Newtonsoft settings: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-9-0

For example the closest equivalent to IgnoreDefaultValues is by setting the DefaultIgnoreCondition value in the settings: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/ignore-properties#ignore-all-default-value-properties.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants