Sadly yes, we have data. We are migrating our C# SDK to Rust in part because customers want a much smaller dependency. And the AoT compiler didn't trimmed as much as we wanted.
(regarding size - there are tools like sizoscope to understand what is taking space, sometimes it’s something silly like rooting a lot of metadata with reflection referencing many assemblies or because of abusing generic virtual members with struct parameters in serialization, obviously if you can use Rust without productivity loss it’s fine, but usually problems like that take an hour or two to solve, or less)
But in either case binary sizes are smaller and more scalable than what Go produces. The assumption that Go is good at compact binaries just does not replicate in reality. Obviously it’s nice to be able not touch it at all and opting into Rust for distributing native SDKs. Go is completely unfit for this as it has even more invasive VM interaction when you use it as dynamically linked library. NativeAOT is “just ok” at this and Go is “way more underwhelming than you think”.
I think we would have preferred continuing with .NET, as no one is Rust expert on the team. But binary size and lack of some SIMD instructions moved the balance to Rust. And then, the PoC had big memory usage improvements, so...
What kind of SIMD instructions were not available? I assume something like AVX512GFNI or SHA x86 intrinsics?
I think if you're in domain of using SIMD, besides base RAM usage of 2-5MB you should not see drastic difference unless you have a lot of allocation traffic. But I guess Rust solved most of this, just wanted to note that specific memory and deployment requirements are usually solvable by changing build and GC settings.
Is AOT compiling your binaries [1] an option for you? The starting size of AOT compiled C# can beat Go in size [2] and from there it really depends on what you do and how you do it. Some simple ASP.NET server with https and routing can comfortably fit under 10 MB and there are compilation options that can help optimize further [3].
> Fair enough; perhaps it'd be worthwhile to throw a compiler error if both reflection and trimming were attempted.
The tooling already generates warnings for any spot in the program that uses reflection _in a way that cannot be statically analyzed_. Fixing code to make it work with trimming is equivalent to fixing warnings. Here are a couple case studies: https://devblogs.microsoft.com/dotnet/creating-aot-compatibl...
I thinking “this is some project probably written years ago and loading the .NET Framework runtime into kernel space” but this is actually using NativeAOT and the latest features/SDK.
ZeroSharp is done by the same person as bflat (me). These ZeroSharp samples get broken all the time because they bend the MSBuild targets that ship with .NET to an extreme. Bflat is a more supportable route and has a couple extra customizations to the compiler specifically for these scenarios.
Some executable file formats have time stamps in them.
bflat follows the platform convention that respects this and writes current date/time in the file header by default.
It is also a platform convention to offer options to set these to something stable (e.g. `/Brepro` on lld-link, or `--deterministic` for bflat) if one wants stable output.
ultra-pedantically: that's true with the "well tempered" tuning that is used almost universally. But this is really just an approximation, and one that our ears have grown used to.
Mathematically speaking, B# <> C. On a piano or other instrument that quantizes tones, we have little choice but to go with the approximation. But for violinists and others that can choose from a spectrum of tones, it's possible to hit those tones exactly. Of course, when playing with others, the violin still needs to quantize just to be in tune with the others.
C#: 945 kB Go: 2174 kB
Both are EXEs you just copy to the machine, no separate runtime needed, talks directly to the OS.