Native AOT Overview

(ericsink.com)

80 points | by luu 17 days ago

11 comments

  • jmillikin 15 days ago
    I used to contribute to a C# project aimed at Linux users[0], and one of the personally annoying aspects was requiring the .NET runtime. It's a lot like how Java programs require a JVM, except worse because OpenJDK is a blessed implementation in a way that Mono never was.

    When I saw that .NET 7 included AOT, I immediately downloaded it to play around. It's clearly not fully baked (missing macOS support), but what is there is great. AOT makes C# a direct competitor to Go for projects that don't need to wring every last cycle out of the CPU, and it doesn't have the same squeamishness about language complexity that characterizes Go.

    The only part of .NET 7 that really bothers me is the compilation process. I couldn't find any compile-to-obj command with flags and such, instead there's a `dotnet` command that takes its input via a monolithic `.csproj` XML file and spits out a fully-formed binary. Trying to integrate C# into a Unix-style build pipeline (make/Bazel) doesn't seem straightforward in the current SDK.

    [0] https://www.banshee-project.org/

    • pjc50 15 days ago
      > Trying to integrate C# into a Unix-style build pipeline (make/Bazel) doesn't seem straightforward in the current SDK.

      Basically, don't do that. "dotnet build" invokes "msbuild" which is a build system analogous to make. If you want a remotely peaceful life, just lay out the project the way Microsoft expect you to and let it do its thing.

      You can invoke Roslyn "csc" from the command line, but some of the more obscure bits of tooling (e.g. building "self contained" executables) are only implemented as .dlls that are msbuild Task objects.

      Msbuild isn't too bad once you've got over the culture shock that (a) everything is XML rather than tab-delimited makefiles and (b) you've got a glossary of terms that maps to things you know https://learn.microsoft.com/en-us/visualstudio/msbuild/msbui... and (c) you get the binary log viewer https://msbuildlog.com/, which is a fantastic tool that gives you total observability of every detail of the build process. Of which there is a lot.

      Separate thought: .net AOT is still very much experimental. All sorts of things are silently incompatible with it, either in the "refuses to build" or the "crash at runtime" way. I'd only use it for small projects built from the ground up to be AOT.

      • anarazel 15 days ago
        > Msbuild isn't too bad once you've got over the culture shock that (a) everything is XML rather than tab-delimited makefiles and (b) you've got a glossary of terms that maps to things you know https://learn.microsoft.com/en-us/visualstudio/msbuild/msbui... and (c) you get the binary log viewer https://msbuildlog.com/, which is a fantastic tool that gives you total observability of every detail of the build process. Of which there is a lot.

        I've only had to interact with it for C[++], but based on that, I'd do a lot to not use it for projects I work on. No-action-needed builds are orders of magnitude slower than with any other tool, the determination of what needs to be rebuilt is ... not accurate, understanding the build rules is hard, there is very little useful documentation, there are a lot of bugs.

      • jmillikin 15 days ago
        I'm satisfied with Bazel as a build system, and some of its functionality that is important to me (sandboxed remote execution) works best when it's not used to delegate to recursively nested sub-builds. To the extent I wish anything different about Bazel, it's to move even further away from integrated language-specific logic[0].

        The idea of trying to learn some new ecosystem-specific build tool (in this case msbuild) is unappealing. Done that too many times, and I'm tired of fighting with tools written for/by people who think projects are written in a single language.

        [0] I hear Buck2 is a step in this direction.

        • iainmerrick 15 days ago
          I’ve been burned by using the original Buck in an app which is now a pain to maintain. It looks like Bazel is the best path for me to get free of it.

          I looked briefly at Buck2 but it doesn’t work yet (for my purposes, at least). Facebook have copied the old Google cliche, “you can use the old version that’s deprecated, or the new version that doesn’t work yet”.

          Edit to add: a million times yes to your general point! Language-specific build systems are bad.

      • andygocke 15 days ago
        > Separate thought: .net AOT is still very much experimental. All sorts of things are silently incompatible with it, either in the "refuses to build" or the "crash at runtime" way. I'd only use it for small projects built from the ground up to be AOT.

        It's certainly true that most things aren't AOT-compatible out of the box -- this is basically due to the heavy ecosystem reliance on reflection.

        BUT, it definitely shouldn't be silently incompatible. We've spent a lot of time building "trimming warnings" (https://learn.microsoft.com/en-us/dotnet/core/deploying/trim...) that should flag any and all incompatibilities.

        If there's some incompatibility, a warning should be generated. If there isn't that's likely a bug on us (https://github.com/dotnet/runtime). But conversely, if there are no warnings, you should be guaranteed that the app should be AOT-compatible.

    • andygocke 15 days ago
      > I couldn't find any compile-to-obj command with flags and such, instead there's a `dotnet` command that takes its input via a monolithic `.csproj` XML file and spits out a fully-formed binary

      FYI I'm the lead of the Native AOT team at MSFT.

      The key thing about .NET AOT is that it compiles the whole framework together, so right now the `dotnet` command is the top-level tool that connects all the pieces together. If you wanted to integrate with other native code, you can use `dotnet` to output a static library, which could then be linked with the rest of your native code.

      You're right that this can be complicated to integrate with other build tooling. It's something we can work on, but there's a bit of inherent friction around the .NET side, where it needs to find all the appropriate libraries, NuGet packages, etc, and a make/bazel world.

      Definitely something we can work on as we go forward.

    • neonsunset 15 days ago
      Self-contained single-file assemblies do not require this. Simply specify corresponding properties in .csproj or as arguments to dotnet publish (-p:PublishSingleFile=true --self-contained -r {RID}) and for pure C# projects you will get exactly a single executable file that does not require any dependencies to run except what is expected to have on the average target system (glibc, openssl, etc.).

      Other than that, it seems that due to other languages, there is misconception that Native AOT is a panacea that is superior in all areas to normal single-file executables that use JIT. In fact, on average, JIT produces faster code because some optimizations are only possible with tiered and dynamic PGO compilation.

      • andygocke 15 days ago
        You're right -- there are a lot of tradeoffs here, and one solution isn't necessarily better.

        What Native AOT offers is very small binaries, with very fast startup, and smaller working set. It can also be integrated as a standalone static/shared library into a different application.

        Self-contained apps don't require AOT compatibility and may have higher peak throughput (as you said, JITing can be faster in some cases).

        It's up to you and your application whether the trade-offs make sense.

    • nerdix 15 days ago
      Loved Banshee. There was a short period of time during the mid 00s where it looked like Mono/C# had a chance to become the dominate platform for gnome apps. Banshee, F-Spot, Beagle, Docky.

      And some really good stuff came out of the Mono as well. Like Mono.Cecil

    • tempodox 15 days ago
      I compile F# to a stand-alone executable that runs on a Linux box without any additional .NET installs:

        dotnet publish /property:PublishSingleFile=true --configuration=Release --self-contained=true
    • fabian2k 15 days ago
      You don't even need native AOT for that, you can already create self-contained binaries with .NET Core. Those have fewer restrictions than native AOT, they bundle the necessary runtime.
      • pjc50 15 days ago
        They have some annoying downsides: they unpack the runtime when you run them. This can cause problems like "virus scanner takes time looking at each .dll unpacked" and "fill up the disk they're unpacked into".

        If they don't fix this by version 8 I might have to build my own CLR apphost solution :(

        • fabian2k 15 days ago
          I don't think that is the case anymore. I think it also was platform-specific before and only applied to Linux. But I couldn't find the exact change with a quick search.
          • pjc50 15 days ago
            It's not the case for AOT, but it is for "self-contained", at least as of .NET 6.
            • ripley12 15 days ago
              I believe this got fixed in .NET 5; now only native libraries need to be extracted and stored elsewhere on disk, and many applications can get by without native dependencies.

              > When the app starts, the managed DLLs are extracted and loaded in memory, avoiding the extraction to a folder.

              https://learn.microsoft.com/en-us/dotnet/core/deploying/sing...

            • fabian2k 15 days ago
              It doesn't extract onto disk, I'm not sure if it is still doing some extraction into memory.
    • aseipp 15 days ago
      I've been wondering how to integrate modern .NET Core into a custom build system (buck2) and was wondering similar things. There's this project I think is cool called bflat[1] that basically makes the C# compiler more like the Go compiler in the sense it's a one-shot single-use tool that can cross compile binaries natively. It's done by one of the people on the .NET Runtime team as a side project, and quite neat.

      I mostly use Visual Studio but I think in practice you're supposed to compile whole .dll's or assemblies all at once, which acts as the unit of compilation; I don't think the csharp compiler generates native object-files-for-every-.cs or translation unit, the kind of approach you'd expect from javac or g++. Someone please correct me if I'm wrong though! I'd like to learn more if there's a more granular/incremental unit of compilation.

      Since Roslyn and the compiler itself are mostly a library, maybe you could also write your own custom "csc-for-bazel" compiler or something to sort of bootstrap the whole thing and offer tighter integration...

      .NET 8 will support Native AOT on macOS, I think.

      [1] https://github.com/bflattened/bflat

      • MarkSweep 15 days ago
        For more incrementalism, you can tell csc to generate reference assemblies. These assemblies contain only the public surface area of the assembly. You can use them to avoid recompiling downstream assemblies when the public API surface does not change. They are similar to ijars in Java.

        https://learn.microsoft.com/en-us/dotnet/standard/assembly/r...

        I’ve been thinking a lot about what a good intervention of .NET and tools like Buck would look like. I think both Buck 1 and Bazel’s rules_dotnet directly target the csc compiler. Given how much logic is written in MSBuild files and how many steps are part of the build (analyzers, illink, ilcompiler), I think it makes sense to target the dotnet executable directly. Having a way to set arbitrary MSBuild properties and items would be nice.

        It would be nice if you could generate Visual Studio projects from the Buck targets.

        Lastly, it would be nice if these rules supported Bazel-style persistent workers. Both MSBuild and csc support a client server architecture, but rules_dotnet does not take advantage of that currently. Performance suffers accordingly.

      • andygocke 15 days ago
        .NET 8 will support macOS, and yeah I think your approach of basically re-implementing much of the SDK logic in bazel is probably your best bet for a reliable experience.

        At the same time, it's obvious that that's a considerable chunk of work.

        Also, IDEs like VS and VS Code are reliant on MSBuild APIs to construct a semantic understanding of the projects, so without MSBuild files, they won't properly function.

        This is an unfortunate problem, which I don't have a simple solution for.

    • MarkSweep 15 days ago
      The .NET SDK has some options to integrate NativeAOT with a larger application. You can reference libraries (static or dynamic) and object files during the build to link into the final executable. You can also configure NativeAOT to generate a static or dynamic library that you can link into another executable.

      https://learn.microsoft.com/en-us/dotnet/core/deploying/nati...

      • jmillikin 15 days ago
        Notice how that page describes how to configure the toolchain using XML. That's the thing I don't like -- instead of just building up an args list like cl.exe, I need to write my own intermediate tooling that synthesizes a .csproj and spits it out into the build sandbox before launching their magic do-everything binary.

        There's clearly something else underneath, the <LinkerArg> element is exactly what I would want to generate when running link.exe, but most of the configuration options are wrapped in a layer of gunk.

        Ideally it would be possible to invoke a command directly to convert a bunch of *.cs files into a .dll (or .a), then I could use existing logic to link it with link.exe (or ld).

        • pjc50 15 days ago
          FWIW, the relevant gunk: C:\Program Files\dotnet\sdk\7.0.100\Sdks\Microsoft.DotNet.ILCompiler\build\Microsoft.NETCore.Native.targets

          And C:\Program Files\dotnet\sdk\7.0.100\Sdks\Microsoft.DotNet.ILCompiler\build\Microsoft.NETCore.Native.Unix.targets (where the linker is selected)

          See the area around and after this chunk

                <CustomLinkerArg Include="&quot;$(NativeObject)&quot;" />
                <CustomLinkerArg Include="-o &quot;$(NativeBinary)&quot;" Condition="'$(TargetOS)' != 'windows'" />
                <CustomLinkerArg Include="/OUT:&quot;$(NativeBinary)&quot;" Condition="'$(TargetOS)' == 'windows'" />
                <CustomLinkerArg Include="/DEF:&quot;$(ExportsFile)&quot;" Condition="'$(TargetOS)' == 'windows' and $(ExportsFile) != ''" />
                <CustomLinkerArg Include="/LIBPATH:&quot;%(AdditionalNativeLibraryDirectories.Identity)&quot;" Condition="'$(TargetOS)' == 'windows' and '@(AdditionalNativeLibraryDirectories->Count())' &gt; 0" />
                <CustomLinkerArg Include="-exported_symbols_list &quot;$(ExportsFile)&quot;" Condition="'$(TargetOS)' == 'OSX' and $(ExportsFile) != ''" />
                <CustomLinkerArg Include="-Wl,--version-script=$(ExportsFile)" Condition="'$(TargetOS)' != 'windows' and '$(TargetOS)' != 'OSX' and $(ExportsFile) != ''" />
                <CustomLinkerArg Condition="Exists('$(_Win32ResFile)')" Include="&quot;$(_Win32ResFile)&quot;" />
                <CustomLinkerArg Include="@(LinkerArg)" />
        • pjmlp 14 days ago
          The old ways of command line arguments are no longer available, nowadays project files have to be used.
    • rubenv 15 days ago
      Fellow Banshee contributor, ex-F-spot maintainer here. Those were good days.
      • sandyarmstrong 15 days ago
        Yup, there was so much excitement around the free desktop as a promising future for computing. I miss it! And I always appreciated your contributions.

        Now we all use unbelievably locked-down mobile devices, nobody even knows what files and folders are, and fewer kids than ever are able to learn about how computers work by messing around under the hood. Other amazing things are happening, but it's sad what gets lost.

        • rubenv 13 days ago
          100% this!

          Hope you're doing well Sandy, been a long time.

    • Rochus 15 days ago
      > annoying aspects was requiring the .NET runtime ... OpenJDK is a blessed implementation in a way that Mono never was

      Which is unjustified, because Mono CLR is just a single executable less than 5 MB which you can download and run without a complicated installation process (see e.g. https://github.com/rochus-keller/Oberon/#binary-versions ). AOT compilation on the other hand is a huge and complex installation depending on a lot of stuff including LLVM, and the resulting executables are not really smaller than the CLR + mscorlib + app.

      • aseipp 15 days ago
        Well, context and history is also probably relevant because for a very long time there was a lot of (mostly unwarranted, at least IMO) fear surrounding Mono and or not it was something Microsoft would care about in the future (or other random things verging on total conspiracy). Banshee is a pretty long-lived project; we might be talking 10+ years ago. These days I think it's pretty clear that both Mono and .NET Core are reasonable and officially battle-tested options.

        Anyway, I'm not sure what Mono's AOT compiler looks like these days, but the new Native AOT for .NET (which is improved in .NET 8) is definitely designed for production and things like binary size and startup speed, with no external dependencies, and they definitely seem to be trying to make it act and feel like other natively compiled AOT languages. People care more about it these days because a big shift is in things like containerized runtimes where you don't want to pay for the overhead of distributing the .NET runtime for each container, because they can't be shared. So things like AOT, dead code elimination, reflection removal for source generators at build time etc all add up. Cold-start time is also important for some of those uses.

        Linux distro fragmentation and userspace ABI compatibility being an afterthought also makes some FOSS/Linux projects that require interpreters/runtimes more annoying than they need to be and that also had some cultural, perceptive ramifications for a long time too -- that's not really .NET specific, but it probably never helped. Like, you're saying "just download an executable and run it" but that was actually often not workable in many cases for many reasons for many apps. And whether or not whether users understand what those reasons were, correctly or not, it colors their perception of that class of tools.

        But anyway. These days there are lots of solutions for these problems. Native AOT is a pretty welcome solution for a good class of issues IMO, and Mono and .NET Core both seem to be established in various domains. I'll definitely be looking forward to trying Native AOT on server-side apps.

        • jmillikin 15 days ago

            > Banshee is a pretty long-lived project; we might be talking 10+ years ago.
          
          I looked through my archives and I think most of my contributions were pre-2010, so 13+ years.

          Mono was a very different thing back then -- less mature, and Microsoft (mid Ballmer era) seemed to have a love/hate relationship with it.

        • pjmlp 14 days ago
          Well to be fair, OpenJDK only became a thing after Java 7.

          It was announced for Java 6, half baked during Sun/Oracle transition, and to this day most people still don't grasp the difference between OpenJDK, other OpenJDK distributions and alternative JVM implementations.

      • afavour 15 days ago
        I think the most important part of the OP's complaint is "blessed". For a very long time Mono was an entirely unofficial reimplementation of .NET for Linux. You can't blame anyone for being hesitant about relying on it.
        • Rochus 15 days ago
          How can an implementation of a standard (ECMA-335 or ISO 23271), which is even explicitly intended by MS to be used by others (even supported by a comprehensive MS "open source" implementation to illustrate it) be "entirely unofficial"?
          • coredog64 15 days ago
            Been a while since I’ve touched the CLR, but I have this vague recollection that while the Mono runtime is blessed, it’s also incomplete. If you want to write a portable CLI you can use Mono, but if you want to show a GUI, you’re out of luck.
            • Rochus 15 days ago
              > if you want to show a GUI, you’re out of luck.

              Mono included a powerful GUI framework which run on far more platforms than the .NET GUI. And Ximian/Xamarin invested a lot of effort to be as compatible as possible to all aspects of .NET (see e.g. https://www.mono-project.com/docs/about-mono/compatibility/).

          • senko 15 days ago
            Because the standard wasn't enough to build a complete app - parts are (were?) missing.

            It was also not explicitly intended, blessed or even recognized by MS for a number of years. It was just another open source project.

            This gave rise to other platforms that were attempting to improve on C (GNOME) / C++ (KDE), such as D, and finally JavaScript for GNOME. Meanwhile, web apps (and then Electron apps) became de-facto defaults due to their cross-platform nature.

            Had MS embraced (err...in a positive way!) Mono from the start, taking into consideration Miguel's influence in the GNOME community, GNOME 3 might have been written in .NET.

            • Rochus 15 days ago
              The standards cover the CLR and core library, i.e. the stuff which is replaced in case of an AOT compiler. The .NET framework is an implementation on top of CLR and the core library, and the Mono project included a version with the intention to be as compatible as possible to .NET, but also an alternative, cross-platform framework, which was/is suitable to build all kinds of applications, including GUIs. Concerning the "blessing" I don't understand why people are less concerned with JDK than with Mono, because the owner of the technology is not exactly known for not enforcing his rights in court (see e.g. https://en.wikipedia.org/wiki/Google_LLC_v._Oracle_America,_....). But fortunately there are limits to copyright when it comes to compatibility.
    • Nathanba 15 days ago
      well nowadays you dont need to add each .cs file separately into the csproj file anymore which means you can pretty much just use the same file for your projects and the only difference are the package references. It's not that far away from a barebones folder but I really agree that the default build should allow me to omit csproj files entirely

      and then maybe even change the default build to always use AOT and you're precisely where golang is

  • ericsink 15 days ago
    (Author here)

    Most of the buzz about .NET Native AOT is focused on things like startup time for compiled executables in cloud environments. For good reason.

    But Native AOT also supports compilation to libraries with a C ABI, including both shared libs and static. My blog series tends to lean in that direction, talking about interoperability.

    Some of the posts talk about very fundamental things. Some of the later posts give mention to a (somewhat experimental) binding generator I've been working on, using CLR metadata to automatically create glue and projections for other languages like Rust and TypeScript.

    In general, interop between C# and other languages has been possible for a long time, but Native AOT allows it to be done without hosting a CLR, and as the feature matures, I think that'll make it more interesting for some use cases.

  • ducharmdev 15 days ago
    I didn't realize macOS wasn't supported, that's a disappointment. I work at a startup that's all .NET 6 on macbooks, and I was pretty excited at the chance to use native AOT.

    I assume running an app in a container wouldn't allow you to sidestep that problem?

    • kryptiskt 15 days ago
      If you want to be on the bleeding edge, the .NET 8 previews supports AOT for macOS.
      • ericsink 15 days ago
        Indeed. I've been using the .NET 8 previews with Native AOT on an M1 Mac for several weeks, and things work quite nicely.
        • ducharmdev 15 days ago
          Oh sweet, I'll probably wait for November when .NET 8 releases but good to know it works well on the M1!
    • neonsunset 15 days ago
      A change to support macOS just barely missed the snap to get in .NET 7 but has been available since early pre-preview builds of .NET 8. Its support is really good in 8 and there will be even a feature to use NativeAOT for 'net8.0-ios' targets meaning you could write components in C# and integrate them into Swift binaries via C bindings (maybe there are better interop options? my knowledge is very limited on this topic).
  • weinzierl 15 days ago
    Every couple of years I look into Java AOT and it always has been awful so far. I wonder if C# will be the same or if they manage to get it right.
    • neonsunset 15 days ago
      There are efforts to ensure that important workloads like ASP.NET Core work with it well. Due to AOT nature it's often not possible to make it require just a build argument switch, but it is still fairly easy nonetheless, in 90% of situations having to do with switching from reflection to source generation (like in the case of System.Text.Json) or solving statically not-analyzable patters of reflection (AOT supports reflection, just not emitting new code in runtime since there's no JIT!).

      Well-written libraries that do not use reflection to generate new code in runtime do not require any changes and "just work". A lot of new code shipped is now written with Native AOT in mind to be compatible (with some exceptions, looking at you, SemanticKernel).

    • brabel 15 days ago
      The current way to compile Java AOT is GraalVM's native-image... that actually works pretty well... do you find that awful??
      • weinzierl 15 days ago
        It looks promising, but so did gcj at its time.

        Still it's not practically usable and that's what counts for me. From what I understand it is plagued with the same issues as the C# AOT when it comes to reflection and therefore serialization. There is a whole list of other issues as well[1].

        I think some of this issues can never be solved by the compiler alone but need to be addressed in the ecosystem. It would be important, for example, that the most common and important libraries dispense with reflection to play well with AOT.

        Finally GraalVM is just a single project with its own specific agenda. If Oracle changes its mind about these goals tomorrow Java AOT will be dead once again.

        These are the reasons I'm still not optimistic about Java AOT, even if GraslVM is really cool project.

        [1] https://www.graalvm.org/22.0/reference-manual/native-image/L...

        • CuriousSkeptic 15 days ago
          They are addressing reflection rather aggressively actually, using source generators mostly.

          Even if I have no direct need for AOT I’m eager for the added type safety and sanity gained by getting rid of reflection, not least for serialisers!

        • pjmlp 14 days ago
          There is PTC, Aicas, OpenJ9 and there used to exist Excelsior JET.

          Also Android Java uses a mix of JIT and AOT, depending on the workloads.

          Hardly dead.

  • zerr 15 days ago
    Does it support any of WinForms/WPF/WinUI3/MAUI or is it just for CLI apps?
  • dhosek 15 days ago
    I was just thinking that if Microsoft were Apple, they’d just design a chip so that the IL code would be native code. I’ve often wondered what it would take for .net’s IL (or Java Byte Code) to be the native instruction set of a processor.
  • Mertax 15 days ago
    How do serialization libraries, like Newtonsoft JSON.net, work on platforms like iOS/Blazor/Unity that use AOT? Doesn’t seem like anything special has to be done other than reference the Nuget package and it’s just working?
    • dellamonica 15 days ago
      There has been a push for using Source Generators to move stuff that relies on reflection to compile time code generation. JSON serialization is (mostly) supported in this mode with perf advantages as well.

      This does not address pre-existing packages obviously. To make them work with AOT you might need to include an XML file (rd.xml) that indicates all the types that could be dynamically generated in your code. It is quite brittle and AFAIK everything might work until you get a runtime failure if something you did not include ends up being constructed.

      • GordonS 15 days ago
        And when stuff does inevitably break once deployed to production, it can be really difficult to pinpoint the reason why.

        I've stopped using AOT, as IMO it's not (currently) suitable for complex, real-world apps - unless you are prepared for a lot of testing and pain.

    • matthew-wegner 15 days ago
      With Unity/IL2CPP stuff: For general-purpose serialization libraries like JSON, you sometimes need to provide hints to make sure types are included: https://github.com/jilleJr/Newtonsoft.Json-for-Unity/wiki/Fi...

      For schema serialization on known types, there are codegen tools (i.e. moc for MessagePack): https://github.com/neuecc/MessagePack-CSharp

      MessagePack is migrating to Rosalyn code generators, so basically invisible codegen. Cysharp's newer serialization library, MessagePack, already uses this: https://github.com/Cysharp/MemoryPack

      • Nathanba 15 days ago
        memorypack looks interesting but the page omits a payload size comparison so who knows how good it really is
    • ledgerdev 15 days ago
      I was able to use aot and System.Text.Json in a test app. You only need to put an attribute on a class to specifying the type info, and then pass a type enum when you serialize/deserialize. I was surprised how easy it was because I had been really concerned about reflection based stuff like json.
    • kryptiskt 15 days ago
      As I understand it, Mono supports reflection when compiling AOT, so serializing using reflection would work.

      With a newish .NET they don't need reflection for AOT code, as JSON libraries can use source generators instead.

  • lowleveldesign 15 days ago
    NativeAOT is an excellent way to expose .NET code to native apps. One big feature I am missing is support for x86. If you have a 32-bit app in which you want to inject your .NET library, NativeAOT won't help.
  • SillyUsername 15 days ago
    What goes around comes around.

    2000s Compile to binary > bytecode (performance, size and startup time important)

    2010s Compile to bytecode > binary (performance important)

    2020s Compile to binary > bytecode (size and startup time important).

  • galonk 15 days ago
    I don't know how it works, but it seems very odd that serialization/reflection wouldn't work with AOT... the information you need is there in memory either way, isn't it?
    • dellamonica 15 days ago
      It can get really tricky: using reflection you could read a string from any input and create a generic type instantiation that never happens in the source code. How would the code for that type be present in an AOT scenario?

      There are also several optimizations that can be made in AOT compiled code that are not allowed when you enable unrestricted reflection or dynamically loading code. As an example, suppose an interface is implemented by just one type in your code. If you AOT compile it, the compiler can safely assume that all calls to methods on that interface actually go to that single type implementing it, thus it can replace interface dispatches with direct calls.

    • MarkSweep 15 days ago
      You can get serialization to work, but you have to use a serialization framework that is trimming compatible. For example, to use System.Text.Json, you have to opt-in the types you want to serialize.

      https://learn.microsoft.com/en-us/dotnet/standard/serializat...

      • sebazzz 15 days ago
        "Opt-in" is not doing this feature justice, that is full serialization code pre-generated at compile time for your types. Beautiful.
    • reubenmorais 15 days ago
    • pjc50 15 days ago
      The default XML (de)serializer does a lot of code generation at runtime. Which doesn't work with AOT.
  • garganzol 15 days ago
    Not really useful because nothing works except the simplest apps. I understand that it is possible to "dumbdown" the code in order to make it work with AOT, but it is too much hassle to even try.

    So, while it was released as a non-experimental feature, actually it still is.