Remix.run Logo
▲ afdbcreid a day ago

C# doesn't (and can't) use WASM GC.

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.

▲xnorswap a day ago | parent | next [-]

Any cross-language benchmarks are always unfair for those kinds of reasons, because ultimately everything is eventually possible against any given hardware.

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.

▲afdbcreid a day ago | parent [-]

> ultimately everything is eventually possible against any given hardware.

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.

▲memsom a day ago | parent | next [-]

> Not everything, no. You can't run C# without the .NET runtime

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.

▲afdbcreid a day ago | parent [-]

Indeed, some parts are possible to strip. Some not.

▲jeswin a day ago | parent | prev | next [-]

> You can't run C# without the .NET runtime

With NativeAOT, same a golang then. NativeAOT has worked wonderfully for us, and in some specific situations faster than Rust.

▲afdbcreid a day ago | parent [-]

> and in some specific situations faster than Rust

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.

▲DannyBee a day ago | parent | next [-]

The last sentence is definitely true in the limit but not always in practice. PGO doesn’t always make good decisions. It should, but it doesn’t. Anyone who has ever used PGO has seen plenty of cases where PGO results in performance regressions versus non-PGO.

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

▲afdbcreid a day ago | parent [-]

You can always not enable PGO of course. What I meant is that I don't expect NativeAOT to do better.

▲jasomill 18 hours ago | parent | prev [-]

True, but there's no reason PGO can't eventually be implemented for native AOT.

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.

▲afdbcreid 15 hours ago | parent [-]

> True, but there's no reason PGO can't eventually be implemented for native AOT.

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).

▲zarlo a day ago | parent | prev [-]

[dead]

▲fweimer a day ago | parent | prev [-]

Is the support gap mostly related to weak references and finalizers? Or is something else missing?

(Obviously those are already pretty significant gaps.)

▲afdbcreid a day ago | parent | next [-]

The most problematic missing feature for both C# and Go is interior pointers. But there are many others.

▲fweimer 20 hours ago | parent [-]

I thought that C# does not have escaping interior pointers, unlike Go. Last time I looked at the CLR, I couldn't find support for interior pointers.

▲debugnik 18 hours ago | parent | next [-]

Only so-called "ref" (and "out") variables/returns/fields can point to the inside of an object. These follow some borrow-checking rules so they can also point into stack variables, thus only ref-like types like Span<T> can hold ref and ref-like fields, and these are always rooted in the stack. But refs into the inside of a class type can escape the function, because they can keep the object alive.

▲afdbcreid 18 hours ago | parent | prev [-]

It does but only on the stack, known as `ref struct`. E.g. `Span<T>` is an interior pointer, and this is extremely common in modern C#.

▲zachsaw a day ago | parent | prev [-]

Those are supported afaics. It's reflection that isn't supported according to the docs.

▲pwpwp a day ago | parent [-]

Wasm GC does not offer weak refs or finalizers at this point: https://github.com/WebAssembly/gc/blob/main/proposals/gc/Pos...

▲zachsaw 15 hours ago | parent [-]

uh the readme itself says it comes with different flavours of precise GC so clearly not using Wasm GC?