WebAssembly Rust Advantage besides size - compared to GC Languages

The size of Rust WebAssembly build being smaller compared to GC (garbage Collector) languages is a great advantage. The smaller runtime without GC would help to download faster.

Besides size, is there any performance benefit when compared to other languages like golang, java, compiling stuff to WebAssembly binary format.

Basically, once you get the WebAssembly binary format would code from all languages including Rust run at the same speed? Again I understand the size difference matters here.

As far as I understand it, rust is not only smaller at runtime, but also faster due to the nature of the code. You specifically mention a GC, which slows the program down at times, because garbage collecting sweeps take up time.

Also, rust will generally optimize heavily, which while true for other languages (Such as C# and Java), I believe that they're JIT (And not AOT), so there's a bit of a performance drawback.

3 Likes

It's not as simple as that. Programs compiled by JITs perform better when running for a longer time, because the JIT compiler can optimize based on the way the code is executed, which an AOT compiler cannot.

For example, if a function Fn(Bool) is always called with the argument true, a JIT compiler can just compile the function with the parameter as a constant (which might remove a lot of checks).

Concerning GC, a major issue with those is that they halt the whole program for a while while they execute. Especially for games and other animating programs, this can lead to hiccups in the framerate (usually about once a second, you might drop a frame). This is very jarring.

There are incremental GCs that avoid this by just stopping the operation if it has taken too long at once and resume at a later time, but this means that the application needs more memory, as data structures might be kept around unused for a longer time.

That said, a GC has the advantage of being able to defragment memory easily, which can make a huge difference when you have a lot of differently-sized allocations.

So, as I said, it's complicated. Everything has their upsides and downsides.

3 Likes

Rust uses a lot of monomorphisation and inlining, so in Rust if you call a small function with true, you're also likely to get optimal code. Recently Rust added support for profile-guided optimization, so I think you can get most of the upsides of JIT even with AOT.

While JITs can optimize hot loops, they usually have to have checks that re-verify their assumptions and bail into cold path if things have changed. I keep hearing how JIT can outperform AOT, but I'm not seeing it in practice on anything larger than a singular hot loop with some specific quirk that only JIT can fix.

9 Likes

I believe that they're JIT (And not AOT), so there's a bit of a performance drawback.

i guess some browsers JIT the wasm itself, but does anything JIT to webassembly from its (webassembly) runtime ? if the JWebAssembly page is anything to go by, it seems to be a static conversion at least
(looks like even GC "required the next version of WebAssembly with GC", is still a work in progress)

so in this case the advantage of AOT compilation without GC is that it already works, for current browsers :smile:

2 Likes

This is just referring to using the browser's GC in wasm memory. You're free to ship your own GC compiled to wasm right now, and some environments (like Unity3D) do so.

Small anecdotal point to add to this mix - in Unity this is worked around by careful pooling, hiding things in scene transitions, lots of testing, and, often enough, hunting down heisenbugs...

That's (much) less of an issue if it's a very casual game or something simple, but it can be a real time-suck on larger projects.

So the advantage of Rust is also, presumably, moving the complexity over from hard-to-predict types of problems to more predictable or at least easier to debug compile-time problems, even where the end result is the same smooth playback.

3 Likes

Yes, but the advantage of GC is that you can defer memory management to a situation where you have the time, such as a scene transition. Since in Rust freeing of memory is scattered around the code, it might be a general performance issue, even when it doesn't cause stutter.

However, maybe it's possible to write an allocator for Rust that just quickly marks freed memory as such and then can clean up (= update its data structures) during a situation when there's more time, which would have to be an explicit call by the application. This way you have a hybrid system, because your allocator doesn't have to scan for references like a GC, but has more freedom in the way it operates than just-in-time management. I haven't seen such a thing yet, though.

Writing modern games is messy.

3 Likes

The question doesn't explicitly mention that you're only looking for performance advantages, so here's another point. There is a great deal of other reasons why one might want to use Rust; for one, it's got a great and strong type system. I don't think that "I need performance" must be the/a necessary justification for using Rust. Correctness almost always trumps performance.

10 Likes

@OptimisticPeach, Yes, I think this answered by question.

WebAssembly binary code for GC languages will have GC included for that language since that is usual for the language to run with GC.
By itself, WebAssembly does not have a GC as we know. Even if WebAssembly later has GC or the language includes the GC, I think Rust that will run without GC will perform better and will not use it.

Also, Well explained by @Pauan in the answer below.

@kornel Agreed.

@anlumo So now in terms of performance in webAssembly, this comes down to Rust vs Other GC languages even without WebAssembly. I think we know advantages of JIT, but in general with Java experience, I think Rust approach without GC will outperform ones with GC. There might be some use cases in GC reaching similar results but still I am biased towards not having GC and rely on Rust memory safety ownership compilation rules. I think that is one of the main reasons Rust exists.

@H2CO3 My question was more advantages of Rust besides size and I see we also have a better runtime since it is without GC. Agree about the correctness but I want both the worlds :wink: at the expense of following though a more strict compilation process.

Thanks to everyone else for the answers.

Overall without GC (resulting in better performance in general), smaller size this explains why Rust (along with C++ I guess to some extent) is leading the path to WebAssembly.:man_dancing:

2 Likes

Small nitpick - you have at least the same level of control in Rust... in Unity you'd have to do a few things - manage when the GC is enabled/disabled, nullify all references, and explicitly run the GC. There's no real notion of deferring that process afaik.

In Rust you just need to do that "nullify all references" part, by causing it to Drop.

If you want to defer that you don't need a custom allocator - you can transfer ownership to some container. Amazingly you can even know if you've really gotten rid of the references at compile-time since there's only one owner. I think Option is typically used for this dummy owner idea, and then when you're really ready to free you can call take() on it (this is the technique used in the reference example for creating a requestAnimationFrame loop)

Of course "simpler" here isn't so simple since that mechanism requires the compiler to know about lifetimes and all that - but if the only criteria is "which language has a better story for memory management", I do think Rust wins on that front.

1 Like

To explain the situation:

WebAssembly allows you to run programs in the web browser at almost the same speed as the desktop.

So if a program or language is slow on the desktop, it will be equally slow in WebAssembly. WebAssembly isn't magic, it cannot make your program faster than it is on the desktop.

So if you take Python and compile it to WebAssembly... it will be the same speed as Python running on the desktop.

Similarly, if you take Rust and compile it to WebAssembly, it will be the same speed as Rust on the desktop.

Since Rust running on the desktop is much faster than Python running on the desktop, Rust running on WebAssembly will also be much faster than Python running on WebAssembly.

That means any speed comparisons between languages on the desktop will (generally) translate over to WebAssembly as well.

So yes, your Rust program will run extremely fast on WebAssembly (faster than most languages), because Rust is quite fast on the desktop.

However, there is an additional factor with WebAssembly which doesn't exist on the desktop: file size.

With WebAssembly the browser must download the program before it runs it, so if the program is 50 megabytes then the user will be staring at a blank screen (or a loading screen) for a long time. So having a smaller file size means the user gets to start using the program sooner.

Rust generally produces smaller WebAssembly files (compared to other languages) because of the following reasons:

  • It doesn't have a garbage collector.

  • It doesn't have a runtime.

  • It has impressive dead code elimination.

  • It has a state of the art compiler (including LLVM) which gives many optimizations, some of which reduce file size.

  • It is designed for embedded programming which requires small file sizes. This means there are many things you can do to dramatically lower the file size.

    A small "hello world" program in Rust compiled to WebAssembly is ~332 bytes. That's impossible in most other languages.

So Rust tends to be smaller in file size, faster in runtime performance, and smaller in memory usage compared to other languages (with the obvious exception of C/C++ which have the same advantages as Rust).

In addition, if there was a situation where a JIT was superior... you could just make a JIT for Rust. So you would have the benefits of AoT optimizations, but also the benefits of JIT.

(Sure, it's easier said than done to "just make a JIT", but it's certainly possible)

4 Likes

Thanks @Pauan for your answer, also referenced it in the Marked Solution above to this question.