C# now beats Rust on WASM size
Posted by sumnole 1 day ago
Comments
Comment by sondr3 1 day ago
[1]: https://www.reddit.com/r/dotnet/comments/1x7wsgb/rust_just_l...
Comment by torginus 1 day ago
Rust also builds a pretty small binary, which is cool. This isn't really meant to be a 'C# is better than Rust' post, but more pointing out that Blazor (which is the official MS implementation) builds huge binaries; often >10MB, which is a real problem. This fixes that.
Comment by giancarlostoro 11 hours ago
Comment by topelinux 1 hour ago
Comment by drhagen 1 day ago
[0]: https://github.com/zion-sati/NetWasm/blob/main/docs/size-and...
Comment by blarelewis741 1 day ago
Comment by xxs 1 day ago
And then the person writing the code has to be an expert in all languages involved to make it even half fair, instead of just a sound-bite/click-bait.
Comment by galangalalgol 1 day ago
Comment by pjmlp 1 day ago
Comment by aw1621107 1 day ago
I think it's also a bit unclear exactly what you mean when you list nonstandard C extensions alongside Rust's no_std and then say "similar approaches". Rust's no_std is part of the language proper so the commonality surely can't be the non-standard-ness of what is done to shrink binary sizes.
Comment by pjmlp 1 day ago
Comment by aw1621107 23 hours ago
That being said, I do think it would have been nice if the author had chosen a more interesting benchmark, but alas...
Comment by CharlieDigital 1 day ago
Good tooling, modern language features (C# 15 has the long awaited unions, closed hierarchies), great debugging experience, etc.
Comment by mhh__ 1 day ago
Comment by xnorswap 1 day ago
I'd like to hear more about your issue with the JIT and where do you find it a hindrance?
I've had a few benchmarks lately that performed better under JIT than AOT compilation, and while that isn't always the case, the JIT, especially on dotnet 10, performs very well.
Comment by jasomill 19 hours ago
Put another way, "100% pure .NET" was never a goal in the same way it was in the Java world, but I never saw this as holding it back any more than the reliance on native code holds back languages like Python, because both have very good integration stories.
My only complaint is the lack of cross-platform support for C++/CLI, as it was a very useful tool for integrating with libraries that lack a non-C++ API.
Comment by mhh__ 14 hours ago
The performance in theory can be slightly better in some niches but my point is simply that it's a huge chunk of complexity for not much gain.
Comment by benterix 1 day ago
Yes, because when you think about it, why should they?
MS has showed many times how unreliable and even user-hostile they can be, so the community at large is very wary of their ecosystem in spite of real advancements.
Comment by tester756 23 hours ago
Comment by afdbcreid 1 day ago
Comment by pdpi 21 hours ago
I wouldn't qualify that with "even". Numerical stuff like that is one of Python's most well-developed niches, and Python's numerical ecosystem is the benchmark everything else needs to be compared against.
Comment by CharlieDigital 1 day ago
C# sweet spot is really web APIs (and all variants thereof), IMO.
Comment by pjmlp 1 day ago
Comment by mhh__ 14 hours ago
Comment by womble2 1 day ago
Comment by pjmlp 1 day ago
Comment by womble2 22 hours ago
Comment by pjmlp 22 hours ago
Comment by womble2 4 hours ago
Comment by SwamyM 22 hours ago
You still get interop with C# and .NET but the syntax and language is superior IMO. If only Ms would invest some resources into it.
Comment by dude250711 1 day ago
Comment by CharlieDigital 1 day ago
Comment by simonask 1 day ago
Once you've tried a better type system (Rust), it's difficult to go back to C#. People jump through all kinds of hoops to get good performance out of it, and they're mostly able to.
Things I dearly miss from Rust when I'm using C#:
- Associated types in interfaces. (IEnumerable should not be forced to return a type-erased = heap allocated IEnumerator instance.)
- Move-only structs. (Currently, every type with value semantics is trivially copyable, even ref structs.)
Comment by kfuse 1 day ago
Foreach loops use compile-time duck typing, so if you don't intend to use your enumerable in LINQ then both the enumerable and enumerator don't need to implement any interfaces.
This optimisation covers only one common use case, but that's the way of things in C# land.
Comment by zarlo 1 day ago
slightly off topic but a lot of things in .NET actually use compile time duct typing. like how async works To make your own task, you don't add an interface to it. You just implement a single method.
Comment by simonask 16 hours ago
Meanwhile, iterators in Rust frequently outperform manual loops.
Comment by jayd16 1 day ago
Its not but if you call from the interface and not the implementation, it has to box because that's the contract. If you for each over a List<T> and not IEnumerable you get a stack allocated value type IEnumerator.
Comment by jasomill 19 hours ago
Comment by simonask 16 hours ago
Like, I get that it’s “fine” for a lot of things, but it’s very not fine in situations where you really need to guarantee that a particular operation doesn’t, say, trigger GC, like in a video game.
Comment by andygocke 16 hours ago
Associated types: https://github.com/dotnet/csharplang/issues/8712
Ownership: https://github.com/agocke/csharplang/blob/ownership/proposal...
Comment by humanfromearth9 1 day ago
Comment by lelanthran 1 day ago
Comment by actionfromafar 1 day ago
If you are doing greenfield, you can also make compilation much stricter with Roslyn analyzers and neutralize some of the bullet points here:
https://yannickloth.github.io/independentvariation/software/...
Comment by gf000 1 day ago
Sure, it's a valid language design, but it's also a negative in "understandability of each line of code", and thus maintainability, and even stuff like tooling, verification.
Comment by CharlieDigital 23 hours ago
Comment by vips7L 1 day ago
Comment by pif 1 day ago
Comment by whizzter 1 day ago
C# is often like Java but without taking a wrong turn when it came to type-erased generics, the generics are uncomplicated enough even if slightly less powerful than what you get with the full instantiation of C++.
C++ was severely lacking before reflection came along in 26, I'm actually writing some new reflection based libraries and it's mostly a joy and quite capable.
But C++ has so many outrightly dangerous semantics when it comes to writing secure code, the coping of pointing to realtime systems,etc as another security "dimension" doesn't hold when Rust,etc exist today.
C# is good in that it's "easy" yet the performance profile of the features allows optimized code to run far closer to C++/rust/etc in performance compared to Java,etc. (that said, a wast majority is probably running like Java due to programmer patterns, but not needing to drop down to another language when it's needed feels good).
Comment by jayd16 1 day ago
If you want to do insane meta programming, you should look into Roslyn generators. It's like they added a robust code gen system to the language (even though its a compile stack feature). You basically get the full power of templates and macros but you get to use the target language (C#) instead of a bolted on macro or template language.
Comment by jasomill 19 hours ago
More generally, the fact that the C# compiler is implemented as library with easy support for runtime code generation is also a powerful feature that I've found to be incredibly useful in a variety of expected and unexpected ways.
Comment by CharlieDigital 18 hours ago
Comment by makotech221 1 day ago
Comment by JamesSwift 23 hours ago
Comment by jayd16 9 hours ago
Greenthreads or what have you are not a replacement if you want to do things like have a UI thread or bind to a native thread for interop. But you _can_ with async/await.
Comment by jasomill 19 hours ago
Comment by JamesSwift 16 hours ago
So yes "async" is easy, but "multithreaded/parallel" is a lot more error prone and explicit.
EDIT: I should also note I havent been in the space for a few years so Im not fully current on how it looks today
Comment by CharlieDigital 16 hours ago
Multi-threaded/parallel is a LOT easier now with the Task Parallel Library (TPL, https://learn.microsoft.com/en-us/dotnet/standard/parallel-p...) and System.Threading.Channels. Channels in particular are nice because it can fan out and then back in to one aggregator, removing the need for self managing synchronization primitives.
I usually default to channels first, TPL when I want threads.
Comment by bjoli 18 hours ago
ConcurrentML really brought dotnet's concurrency story into the 1990s.
Comment by fortran77 1 day ago
Comment by zachsaw 5 hours ago
Rust would be too expensive Engineering wise even with AI today.
Comment by TazeTSchnitzel 1 day ago
what does this even mean
Comment by NewsaHackO 1 day ago
Comment by ActionHank 1 day ago
Comment by aqfamnzc 1 day ago
Comment by tancop 21 hours ago
That's effectively hardcoded if the model is closed source and you're a customer. The only thing you can maybe do with it is put complex jailbreak style prompts in every conversation and that's not really practical.
Comment by jasomill 19 hours ago
Comment by dionian 1 day ago
Comment by dude250711 1 day ago
Comment by Scaled 1 day ago
Comment by zarlo 1 day ago
Comment by subset 16 hours ago
Comment by weinzierl 1 day ago
Since WASM ships with a garbage collector that C# can use while Rust does all the memory management itself, of course it will be bigger.
EDIT: Reading OP more thoroughly I think I'm wrong. Apparently in the compared versions, the C# version ships a runtime with garbage collector.
Comment by afdbcreid 1 day ago
The comparison is unfair for other reasons (the C# runtime was optimized for WASM size and the Rust not, it's possible to be much smaller in Rust), but it's still cool.
Comment by xnorswap 1 day ago
What matters more for comparing what you get out of languages is the output of idiomatic code written against typical frameworks.
Indeed, it's still cool to see, even if it's optimised to beyond recognition, as seeing the occasional C# win is nice to keep the "re-write in rust" wolves from the door, who seem to ever lurk, especially in a world where re-writes appear easier.
Comment by afdbcreid 1 day ago
Not everything, no. You can't run C# without the .NET runtime. Some parts of it are irreplaceable.
And the Rust optimizations to shrink binary size (see https://github.com/johnthagen/min-sized-rust) are commonly done in production when it matters, not just in WASM but also in embedded.
Comment by memsom 1 day ago
You can compile C# to an AOT exe, and when you do that it will literally only be what it needs to work, less and dynamic libs.
Comment by afdbcreid 1 day ago
Comment by jeswin 1 day ago
With NativeAOT, same a golang then. NativeAOT has worked wonderfully for us, and in some specific situations faster than Rust.
Comment by afdbcreid 1 day ago
I'm very curious to hear when? Because even the people that argue managed languages can be faster than unmanaged, say it because the JIT has better information at runtime. But NativeAOT cannot be better (and will be worse) than LLVM with PGO.
Comment by DannyBee 1 day ago
some of these are consistent enough that there are workloads where PGO does worse than non-PGO.
they can all be fixed, of course. which is why what you said is true in the limit, but it might not be true for right now
Comment by afdbcreid 1 day ago
Comment by jasomill 19 hours ago
Microsoft already has both dynamic PGO in .NET JIT and traditional PGO in its C++ toolchain, after all.
And for that matter, there's no reason LLVM itself couldn't eventually be a target for native AOT.
Comment by afdbcreid 16 hours ago
It already is implemented, I believe. The thing I wondered is how NativeAOT could be faster, which is what GP claimed.
(Also, practically it should be slower because NativeAOT is based on the JIT and the JIT is worse at optimizing than LLVM).
Comment by zarlo 1 day ago
Comment by fweimer 1 day ago
(Obviously those are already pretty significant gaps.)
Comment by afdbcreid 1 day ago
Comment by fweimer 21 hours ago
Comment by debugnik 19 hours ago
Comment by afdbcreid 19 hours ago
Comment by zachsaw 1 day ago
Comment by pwpwp 1 day ago
Comment by zachsaw 16 hours ago
Comment by gwbas1c 1 day ago
Is this intended primarily for offloading CPU-intense calculations, or sharing code with a C# backend?
Otherwise, what's the intended story for UI? Do all that in JavaScript?
Comment by andsoitis 1 day ago
Comment by dev_l1x_be 1 day ago
Comment by edave64 1 day ago
Comment by arbor-group 1 day ago
Comment by dortesedixson 1 day ago
Comment by sagacity 1 day ago
Comment by jedisct1 1 day ago
If you care about WASM performance, size and memory usage, Zig is a much better candidate.
Comment by flohofwoe 1 day ago
Comment by WhereIsTheTruth 1 day ago
Comment by VimEscapeArtist 1 day ago
Comment by hvb2 1 day ago
Comment by kcb 1 day ago
Comment by bel8 1 day ago
Do you really just ask the model to pick any backend stack and pray it works?
Comment by kcb 1 day ago
Comment by FrustratedMonky 1 day ago
Comment by Recurecur 18 hours ago
Comment by bob1029 1 day ago
Unless you are building a web version of autocad or premiere, I don't see the point of engaging with this technology.
Comment by pjc50 1 day ago
Yes, but that's the use case. I don't think you'll be able to find someone respected saying "yes we should use this for all use cases regardless of what you're doing". At the other end, htmx is completely fine.
Comment by bluegatty 1 day ago
A lot of edge cases they'd rather run WASM then a JS or Python VM, it has nicer containerization characteristics. But even with those key cases - it's not necessarily a core, critical technology.
WASM is 'always on the horizon' as being important.
If we had in 2015 what we had today, there might have been a bit of a breakthrough.
Comment by ChrisRR 1 day ago