Skip to content

Questions and answers

Daniel Wirtz edited this page May 26, 2019 · 24 revisions

A collection of answers to questions that appeared over time.

Can I just compile [insert existing TS library] to WebAssembly?

Probably not. Pretty much every existing TypeScript program still includes too much JavaScript to do that, and AssemblyScript has to drop a significant amount of the dynamicness of JavaScript so it can compile ahead-of-time efficiently. It is possible, though, to compile reasonably strict TypeScript libraries after making the necessary modifications according to limitations, while still being able to compile the exact same code to JavaScript with tsc. Rule of thumb here is that it's easier to do this for (compute-heavy) algorithms than entire programs that interact with the JS-world (like passing strings back and forth, or interacting with DOM-APIs). It's expected that this becomes easier as WebAssembly develops (i.e. the inclusion of reference types, GC integration etc.).

Is WebAssembly always faster?

No, not always. But it can be. The execution characteristics of ahead-of-time compiled WebAssembly differ from just-in-time compiled JavaScript in that it is more predictable in a way that enables a WebAssembly program to remain on a reasonably fast path over the entire course of execution, while a JavaScript VM tries hard to do all sorts of smart optimizations before and while the code is executing. This implies that a JavaScript VM can make both very smart decisions, especially for well-written code with a clear intent, but might also have to reconsider its strategy to do something more general if its assumptions did not hold. If you are primarily interested in performance, our rule of thumb (that is: from an AssemblyScript perspective) is:

Scenario Recommendation
Compute-heavy algorithm Use WebAssembly
Mostly interacts with the DOM Mostly use JavaScript
Games Use WebAssembly for CPU-intensive parts
WebGL Depends how much of the program is calling APIs, and how much is actual computation on the CPU. Probably both.
Websites, Blogs, ... JavaScript is your friend

Or: WebAssembly is great for computational tasks, stuff with numbers, but still needs some time to become more convenient and efficient (as in: at the same time) where numbers aren't enough.

How does AssemblyScript compare/relate to C++/Rust?

First and foremost: Both Emscripten (C++) and Rust have very mature tooling to compile to WebAssembly and are made by the smartest people on this field. Also, both can make use of compiler infrastructure that has been created by many individuals and corporations over years. In contrast, AssemblyScript is a relatively young project with limited resources that strives to create a viable alternative from another perspective.

More precisely: AssemblyScript is putting anything web first and then figures out how it fits with WebAssembly. Fortunately, there is Binaryen, a compiler infrastructure and toolchain library for WebAssembly primarily created by the main author of Emscripten, that we can utilize to considerably close the gap, and we are very thankful for that. It's not as optimal for AssemblyScript-generated code as it is for LLVM-generated code in a few cases, but it's already pretty good and continuously becoming better. It's also noteworthy that AssemblyScript is still behind in specific language features, especially when it comes to OOP, or even general compiler design - but we are working on that.

In short: AssemblyScript differs in that it is new and tries another approach. It's not as mature as Emscripten and Rust, but there is something about the idea that is definitely appealing. If you find it appealing as well, AssemblyScript is for you.

Is it possible to access memory before instantiation is complete?

If you need external access to memory while top-level code of your program is executing, either import memory or use the @start decorator so you can get a hold of it early. By annotating a top-level function with the @start decorator, any top-level statements of the program will not execute until the first time this function is called. Also means that the first thing to do with a module must be calling this function. This is useful in scenarios where memory isn't imported but top-level code already prints strings for example, as there is no way to access memory until module instantiation is complete otherwise.

How much does the runtime add to a binary?

The full runtime adds about 2KB to your binary after optimizations. This includes a proper memory manager and a complete reference counting implementation with deferred cycle collection. Where external creation of managed objects is not necessary, i.e. where no higher-level interaction with the JS-space takes place, this can be reduced by using the half runtime instead, which doesn't have any implicit exports so the optimizer can more easily eliminate dead code. In scenarios where free/GC support isn't required, for example in very short-lived programs or programs with hardly any memory footprint, the stub implementation can be used instead that only provides a very basic (also: fast) arena memory allocator without any means of freeing up memory again. Similarly, the none runtime is the same as stub but without any exports, essentially evaporating entirely after optimizations.

What's the difference between an if/else and a select?

Semantically, a select differs from an if/else in that both alternatives are always executed, then picking one of them depending on the condition. So if one side has side effects, it is going to happen regardless of the condition. Performance-wise, a select (conditional move, works similar to a switch) is expected to be faster than an if/else where the condition is random (that is, branch prediction is not going to perform well) and the operands are cheap. An if/else where branch prediction is doing well, for example where checking a configuration value that is always the same, is expected to be fastest.

 

Clone this wiki locally