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

native aot announcement #4

Merged
merged 4 commits into from
Feb 3, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
a couple edits
  • Loading branch information
gregsdennis committed Feb 3, 2024
commit 8c6038999c6c4f1c2803fc7115b8997717e1a93c
Binary file modified .jekyll-metadata
Binary file not shown.
16 changes: 9 additions & 7 deletions _posts/2024/2024-02-01-native-aot.md
Original file line number Diff line number Diff line change
@@ -10,25 +10,25 @@ I don't even know how to begin this post. I don't think there has been as big a

***HUGE*** thanks to [Jevan Saks](https://github.com/jevansaks) for the help on this. This update wouldn't be possible without him. Saying he coded half of the update would be underselling his contributions! More than code, though, he helped me better understand what all of this AOT stuff is and the APIs that make it work.

Additional thanks to Eirik Tsarpalis, who basically **is** _System.Text.Json_ right now, for helping shed light on intended patterns with serializer contexts.
Additional thanks to [Eirik Tsarpalis](https://github.com/eiriktsarpalis), who basically **is** _System.Text.Json_ right now, for helping shed light on intended patterns with JSON serializer contexts.

## What is Native AOT?

[Native AOT](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/), or _Ahead of Time_ compilation, is a way to make .Net applications run anywhere using native code. That means they don't need the runtime to operate.
[Native AOT](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/), or _Ahead of Time_ compilation, is a way to make .Net applications run anywhere using native code, which means they don't need the runtime to operate.

What that means for developers who want to _make_ native apps is generally avoiding dynamically generated code, so mostly no JIT (just-in-time compilation) or reflection that involves generics. You can start to imagine how limiting that can be. It makes things especially difficult for operations like serialization, which traditionally relies heavily on reflection.

However, the _System.Text.Json_ team is pretty smart. They've figured out that they can use source generation to inspect any code that might be serialized and generate code that stores the type information, all at compile time. But they can't do that without your help.

First, you have to mark your project as AOT-compatible (the source generation stuff can be done outside of AOT). Then you have to set up a serializer context and annotate it with attributes for every type that you expect to serialize. This is the trigger for the source generation. Lastly, any usage of a method which uses unsupported reflection will generate a compiler warning, and then you have some attributes that you can use to either pass the warning on to your callers or indicate that you understand the risk.
First, you have to mark your project as AOT-compatible (the source generation stuff can be done outside of AOT). Then you have to set up a serializer context and annotate it with attributes for every type that you expect to serialize. (This is the trigger for the source generation.) Lastly, any usage of a method which uses unsupported reflection will generate a compiler warning, and then you have some attributes that you can use to either pass the warning on to your callers or indicate that you understand the risk.

Of course there's a lot more to understand, and I don't claim that I do. So go read the .Net docs or a blog post that focuses more on the power of Native AOT to learn more.

## Why support .Net 8 explicitly?

My understanding was that there were a lot of features in .Net 8 that I didn't have access to when building only to .Net Standard 2.0. Primarily, the compiler only gives the AOT warnings when building for .Net 8. Since that was the goal, it made sense to include the target explicitly.

What was unclear to me was that the majority of the features that I wanted to use were actually in available through either later versions of the _System.Text.Json_ Nuget package or through [Sergio Pedri
What was unclear to me was that the majority of the features that I wanted to use were actually available through either later versions of the _System.Text.Json_ Nuget package or through [Sergio Pedri
](https://github.com/Sergio0694)'s amazing [PolySharp](https://github.com/Sergio0694/PolySharp) package.

> I had at some point tried to update to _System.Text.Json_ v7, but I found that a good portion of the tests started failing. I didn't want to deal with it at the time, so I put it off.
@@ -38,7 +38,9 @@ What was unclear to me was that the majority of the features that I wanted to us

I've had a [long-standing issue](https://github.com/gregsdennis/json-everything/issues/390) open on GitHub where I considered the possibility of dropping .Net Standard support and moving on to just supporting one of the more modern .Net versions. In that issue, I floated the idea of updating to .Net 6.

While that issue languished for almost a year, I had users approach me about supporting features that were only supported in later versions of frameworks. That meant that I'd have to multi-target. I've multi-targeted in libraries before, and I've seen in other libraries the code reading nightmare that can result from a bunch of compiler directives trying to isolate features that were only available in different .Net versions. Trying to read through all of that to isolate what's actually compiling under a given framework target can be tough.
While that issue languished for almost a year, I had users approach me about supporting features that were only available in later versions of frameworks, which meant that I'd have to multi-target.

I've multi-targeted in libraries before, and I've seen in other libraries the code-reading nightmare that can result from a bunch of compiler directives trying to isolate features that were only available in different .Net versions. Trying to read through all of that to parse out what's actually compiling under a given framework target can be tough.

The springboard for this effort really came from Jevan's jumping into the deep end and starting the update by creating a PR. This was the kick in the pants I needed.

@@ -55,11 +57,11 @@ We started updating all of the package references and addressing the immediate w

Then we added `<IsAotCompatible>` properties to all of the library project files, which gave us our first round of AOT warnings to address.

We went through almost 40 PRs between Jevan and me, incrementally updating toward a final state. There was a lot of experimentation and discussion over patterns, and I learned a lot about the AOT APIs as well as finding some solutions to a few pitfalls.
We went through almost 40 PRs between Jevan and me, incrementally updating toward a final state. There was a lot of experimentation and discussion over patterns, and I learned a lot about the AOT APIs as well as finding some solutions to a few pitfalls. I can't tell you how many approaches and workarounds we added only for them to ultimately be removed in favor of something else. But it was part of the learning process, and I don't know that we could have reached the final solution without going through the alternatives.

It wasn't all adding code, though. Some of the functionality, like the `JsonNode.Copy()` extension method wasn't needed anymore because the updated _System.Text.Json_ provides a `.DeepClone()` that does the same job.

By the end of it we were left with just about everything supporting Native AOT. And, mostly thanks to PolySharp, we didn't need to litter the code with compiler directives. The only project that explicitly doesn't work in an AOT context is the schema generation, which requires high levels of reflection to operate. (But really, I consider that to be more for development tools rather than a production library; it's supposed to give you a start.)
By the end of it we were left with just about everything supporting Native AOT. And, mostly thanks to PolySharp, we didn't need to litter the code with compiler directives. (I was even able to remove the dependency on _Jetbrains.Annotations_!) The only project that explicitly doesn't work in an AOT context is the schema generation, which requires high levels of reflection to operate. (But really, I consider that to be more for development tools rather than a production library; it's supposed to give you a start.)

## Is there anything to watch out for when updating to the new packages?

Loading