Thursday, April 4

WebAssembly: What Is It And Why Should You Care?

If you keep up with the field of web development, you may have heard of WebAssembly. A relatively new kid on the block, it was announced in 2015, and managed to garner standardised support from all major browsers by 2017 – an impressive feat. However, it’s only more recently that the developer community has started to catch up with adoption and support.

So, what is it? What use case is so compelling that causes such quick browser adoption? This post aims to explain the need for WebAssembly, a conceptual overview of the technical side, as well as a small hands-on example for context.

What is WebAssembly?

Javascript has been around since the 90s. To be honest, it’s done a pretty good job; whilst it certainly has its drawbacks, it’s succeeded at creating an interactive modern web, even escaping from the browser more recently.

But, inevitably, the type of applications being served up on the web today are far more demanding than could have been imagined when Javascript was first created. Computationally intensive applications and tasks like 3D graphics, video/music editing and VR/AR have traditionally only been able to run natively, because Javascript inherently doesn’t have the performance capabilities to support them.

But what if it were different? What if there was a way to run low-level, optimised code in the browser with near-native performance? That’s exactly the mission statement that WebAssembly aims to fulfill, and it’s a pretty compelling one.

All this talk can evoke grumblings from those who argue that intensive applications are still best off running natively. But ultimately, no-one can deny the convenience that web applications provide end users – no installation means no disk space eaten, no local security worries, and no installation process. It also provides a large amount of appeal to developers, who only need to deal with one platform. Code only needs to be written once, and is far easier to support and maintain.

WebAssembly was created with these high-level goals:

  • Harness common hardware capabilities, be portable and efficient
  • Produce modular binaries which use imports and exports in a similar way to Javascript objects
  • Support non-browser embedding
  • Integrate into the existing web platform (enforce the same security policies, access browser functionality through the same APIs available to Javascript etc)

Note: if you’re interested in reading more about the security of WebAssembly, the official docs provide a good overview, and [Lin Clark] has done a great deep dive on memory access.

How does it work?

Designed to operate at the lowest possible level without compilation for a specific platform, WebAssembly runs directly in the browser, and is the second ever language to be directly understood by browsers. This means that whilst it doesn’t produce machine code, it’s at a low enough level that the browser needs to do very little work to execute it.

WebAssembly comes in two flavours, the .wat text file, and the .wasm raw binary. The two are exactly equivalent and exist purely for convenience; binary code is shipped to the browser but text files are human-readable. Note that sending binary code to the browser instead of Javascript source files also has benefits for the page download size.

Whilst it’s perfectly possible to write WebAssembly yourself, you probably don’t need to, just like you don’t always write in your favorite CPU’s assembly language. WebAssembly is designed to be a compile target for languages like C/C++ and Rust.

The end result is that you can expect WebAssembly to execute at pretty similar speeds to a native application – usually just 10-20% slower. Of course, it’s harder to put a figure on how much faster than Javascript it is, since it depends heavily on the use case and platform.

Because existing C/C++ codebases can be compiled to WebAssembly, it’s easy to re-use program logic when porting from native applications to the web. That’s what Autodesk did with web.autocad.com. They were able to use the same C++ core from their existing desktop apps, and slot the WebAssembled version into a Javascript UI, meaning the AutoCad editor is now available in the browser. The beauty of this is that the C++ core can continue to be improved and debugged by C++ developers who don’t need to know anything about Javascript or the web UI.

How does it compare to asm.js?

You might be reading this and thinking that some parts sound familiar. You’d be right: asm.js has been around for a while now, and also allows cross compiling C for the browser. Asm.js is just an optimised subset of Javascript which the browser has to do less work to interpret/execute. It’s an approach which certainly works, but is essentially a hack around the limitations of Javascript. It also suffered from the problem that it was never standardised, so performance improvements were inconsistent across different platforms.

WebAssembly automatically supersedes asm.js, simply by being a standard directly understood by browsers. It’s faster, more consistent, quicker to download and easier to cache that asm.js.

Figma moved their online editor from asm.js to WebAssembly and talk about the benefits in this article, which is worth reading if you’re interested in the finer details of how they differ.

How do I use it?

Now we’ll walk through a super simple example of using WebAssembly, to illustrate the mechanics of the process. Let’s suppose that we have some computationally intensive graphics code which is written in C, which we want to be able to use in the browser.

#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int compute(int a, int b) {
    // Loads of maths
        return a + 2 * b;
}

For this simple example, all we need to do is include emscripten and let it know that we want to export the compute function. Emscripten is what we’ll be using to compile our source code to WebAssembly. It’s a powerful tool, which does a lot of work behind the scenes to make things run smoothly – from simple things like generating helper HTML and JS files, to automatically converting OpenGL calls to WebGL and providing different levels of build optimisation. We can use it to compile our example like so:

emcc graphics.c -o graphics.js -O3 -s WASM=1 -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]' 

There are a few flags to say that we’re compiling with aggressive optimisation, to WebAssembly, and want to let Emscripten know that we need access to the cwrap function (more on that in a bit).

This compilation process produces a binary graphics.wasm file as well as a handy graphics.js Javascript “glue” file, which contains some conveniently generated helper code for quickly getting off the ground with our new WebAssembly module.

Let’s now go ahead and create a page which uses this newly compiled module.

As you can see, the helper file graphics.js makes it trivial to use our module. After including graphics.js, we simply use cwrap to bind our compiled compute function to a compute function in the Javascript namespace. Then we’re free to use it as we would a normal Javascript function – in this case we just update an HTML element with the result of calling the function. You can see below that the result is as we would expect.

It’s clear that for a simple example like this, it takes very little effort to import a compiled WebAssembly module which contains functions that can be used interchangeably with Javascript equivalents in many cases. This is part of the conceptual appeal of WebAssembly; as it matures and becomes more widely used, it will be easy to find and make use of libraries which implement optimised code with virtually no extra overhead for the developer. You might even end up using libraries built on WebAssembly without even realising.

Conclusion

We’re certainly likely to see more of WebAssembly in the near future. For the majority of sites, it’s not something which is required or even useful, but it’s nice to know that the capabilities are there if higher performance is needed or existing C/C++ code could be reused. WebAssembly is simply the natural evolution of the browser to keep pace with the experiences that developers want to deliver on the web – that can only be a good thing.

No comments:

Post a Comment