Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Come here to us in .NET land, we have even smaller AOT binaries, nullability and better systems programming story. And type unions, whenever they come around in one of the next releases.


How small can a .NET binary for a CLI application be using .NET? Any special instructions for compiling one? (Sorry that this is offtopic but I had the impression that .NET binaries were big so I thought I'd take this opportunity to ask... I like F#)


Short answer: 1-1.3MiB AOT, down to ~800KiB if you add flags to really push it (impractical). Will grow as you add dependencies. ~130KiB for runtime-less JIT, ~13MiB for JIT+runtime. All these imply a single runnable executable you can ship to user as is.

To get this just type `dotnet new console --aot && dotnet publish -o .` in a folder of choice. The binary and .csproj will have the name as the folder they are placed in. You can rename Program.cs too if it bothers you.

Long answer:

There are 3 main ways to publish a binary for a CLI (as well as back-end and often GUI too). .NET is quite flexible about this, which comes down to what you need:

- AOT which starts at 1-1.3MiB, this is what you get out of `dotnet new console --aot` template compiled with `dotnet publish -o .`

- JIT+runtime which starts at 12-13MiB, this is a combination of flags (which frankly would make a good default) trimmed, self-contained and single-file. Normally you get those by specifying them in .csproj or just doing `dotnet publish -o . -p:PublishTrimmed=true -p:PublishSingleFile=true`.

- JIT without runtime (expects the host to have .NET runtime installed) which starts at 120-140KiB. Notably, it's just a thin runtime launcher with pure CIL assemblies embedded in it. This can be achieved with `dotnet publish -o . -p:PublishSingleFile=true --sc false`.

All these have their own use cases that determine which one is the best. Usually, for CLI you either want to use the first or the third one. My personal preference for all kinds of on-off utilities is AOT as it has the best startup time. There are other ways to publish a binary, including the historical default which dumps all assemblies separately, but I think they are not useful given the nature of your question, nor something you need to deal with in practice.

For more comprehensive comparison of what to expect from .NET AOT, you can look at https://github.com/MichalStrehovsky/rt-sz/issues/63 which is a job that tracks binary size improvements/regressions from runtime contributions with the exact data for different templates and sample use cases (full vs stripped down console hello world, asp.net core webapiaot template, avalonia template, etc.)

Overall, what I meant by "smaller binary sizes" is that .NET's AOT tooling has become quite advanced over the last two releases, and provides better scalability as you add dependencies than Go due to metadata compression, dehydrated binary sections, flow analysis, etc.

To give you an example, there's https://github.com/codr7/sharpl that was on HN not so long ago, when compiled with .NET 9 RC.1 it takes about 2.6MiB on my machine.

On F# - 'FSharp.Core' has quite a few dated bits inside, and custom metadata, both of which are not very friendly to linking and AOT compilation size - it will produce trim warnings which means that there is code that might have been "trimmed away" but might be dynamically accessed at runtime, causing an exception. This is normally addressed by using one of the JIT options. Mind you, they still have good startup latency, just not the <100ms one.


Thanks for all this info! I appreciate it.


Honestly decided to focus of Swift server ecosystem is lacking but I really like the lang




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: