Archive

Posts Tagged ‘wasm’

A look at asm.js and the future with WebAssembly

December 30, 2015 3 comments

Earlier this year the WebAssembly [1][2] project was announced, describing itself as “a new, portable, size- and load-time-efficient format suitable for compilation to the web”. It’s a W3C community group project, headed by representatives of all major browser developers. It follows similar efforts by Mozilla (asm.js) and Google (NaCl, Native Client) to create a bytecode format to run on browsers. This would complement the existing JavaScript runtimes, while adding much desired features like real multi-threading, local variables and a massive speed boost.

Since the late 90s I have used web-based technologies in both a hobby and professional fashion, observing how a mostly static web began to move towards adding as much scripting as possible to any page, first using Visual Basic Script, JavaScript, ActiveX and Java, then basically just JavaScript. This half-baked language grew from a quick addition by Netscape to keep up with the competition into the center point of the modern web, being forced into roles it was never meant or designed for.

Since that time JavaScript runtimes have become modern wonders of JIT VM implementations, minimising parsing times while dealing with JavaScript’s idiosyncrasies in such a way to maximise performance. There is however no denying that having a text-based scripting language is much slower than starting off with native code, or bytecode for that matter. This is the reasoning which underlies these efforts by Google and Mozilla to respectively use native code and JavaScript as assembly language.

While Google’s NaCl effort is a fairly straightforward implementation which allows a limited set of the native platform’s code (x86/x86-64, ARM or MIPS) to be executed in a sandboxed environment, Mozilla’s asm.js efforts aimed to use JavaScript as an intermediate (almost bytecode) language, providing a mapping from C/C++ code to a subset of JavaScript. The benefit of the asm.js approach is that it runs in virtually every modern browser, while NaCl requires extensive browser support.

With all of this in mind I decided to look at asm.js from the mindset of an experienced C/C++ developer to see just how far one can push this system.

The first hit by harsh reality comes when you realise that there is only a relatively small sub-set of C/C++ code which can be compiled for an asm.js target. Things which do not work include threads, checks for endianness, low-level features such as longjmp [3]. Beyond this, 64-bit integers are to be avoided since JavaScript doesn’t have a native type for this. The more unsettling limitations appear when one considers the further limitations [4]:

  • No access to a non-sandboxed filesystem (except when running inside node.js with the right configuration).
  • No main loop. JavaScript uses cooperative multi-tasking, so a script cannot run indefinitely or wait for input.
  • Networking is limited to Websocket-only unless you bridge with the JavaScript side.
  • Function pointer handling is… hairy at best, broken at worst. [5]
  • Everything is compiled into one massive JavaScript file, including libc and other system libraries.

These limitations are due to the limitations of JavaScript and its runtime. JavaScript is a single-threaded, prototype-based language with no real concept of scoped variables and the like.

To circumvent the issue of not being able to have a main loop, the Emscripten toolchain [6] requires one to register a function to be called to simulate a loop, with the parameters allowing one to specify how often it should be called in total and per second:

void emscripten_set_main_loop(em_callback_func func, int fps, int simulate_infinite_loop)

Furthermore, one can tweak the behaviour of this simulated main loop with further commands, getting something which somewhat approaches a main loop as one would have in most applications [7]. If one intends to use the same code across multiple targets including asm.js, it rapidly becomes clear that one has to liberally use preprocessor statements to check for the Emscripten (emcc/em++) compiler, as detailed in the Emscripten documentation:

int main() {
//...
#ifdef __EMSCRIPTEN__
  emscripten_set_main_loop(one_iter, 60, 1);
#else
  while (1) {
    one_iter();
    // Delay to keep frame rate constant (using SDL)
    SDL_Delay(time_to_next_frame());
  }
#endif
}

// The "main loop" function.
void one_iter() {
  // process input
  // render to screen
}

This is a rather minor adjustment, one may say. Depending on the application one wishes to port to asm.js it might be the worst of concessions one has to make, but the real struggle comes when one wants to do something beyond merely using the built-in LibSDL support to draw to the screen and handle local events.

Emscripten comes with a range of APIs which match or approach/equal standard desktop APIs, such as its audio support (via HTML5 audio) or networking (Berkeley socket API, with limitations). Hereby the latter is easily the most restricted, as one of the things which one does not get with asm.js and thus Emscripten is access to raw sockets, including standard TCP/UDP sockets. Instead Websocket is all one gets.

What this means is that you can only use ‘TCP’, non-blocking sockets with the sockets one creates. All data being sent and received by the asm.js application is further encapsulated by the Websocket protocol, meaning that any server or client one tries to communicate with also has to speak Websocket protocol. While the WebRTC protocol has been added to the project in the past, this implementation is currently non-functional due to issues. In short, networking with asm.js is quite crippled compared to what one would be used to on most other platforms.

A related limitation here is one of an async nature, such as when one uses functions like sleep() in C. In plain JavaScript an approximation of this would merely block the JavaScript runtime [8]. An experimental feature in Emscripten allows one to approximate the intended behaviour, enabled when calling the emcc/em++ compiler with the ‘-s ASYNCIFY=1’ flag passed to it. This feature is crucial to make software like for example ncurses and similar UI-oriented software work correctly.

Originally I had set out to write a simple terminal application in asm.js, with remote server communication and ncurses functionality. After poking at and experimenting with various approaches I found myself rather disappointed in the limitations of the asm.js platform. I realise that entire games and their engines have been ported to asm.js, and I do not say that it is impossible to do so. Merely that is a lot more work than one would assume at first glance. Sadly it’s not so much abou tmerely switching compile targets from native to asm.js, but rather tweaking one’s code to work with this eccentric and highly limited target platform.

After this run-in with asm.js, I figured that I might as well look at the state of WebAssembly (WASM). Despite being announced half a year ago, the Minimum Viable Product (MVP) goal is not close to being reached, with the actual bytecode format for WASM and related design features still in flux. The most recent status update I found on a Mozilla blog, from earlier this month [9].

Even at this early stage, one can already tell that it is far more like Google’s NaCl than asm.js. One very welcome, upcoming feature is that of having threading support [10], as well as real exception handling capabilities. At this point the WASM project is still hampered by having to use a polyfill approach, which simulates the WASM runtime capabilities in JavaScript, giving it the same limitations as asm.js.

In summary, while I can see promise in WASM, I feel that asm.js at the very least is quite overhyped. It’s so limited and specialistic that beyond porting games to run inside a browser using WebGL and LibSDL, it’s hard to think of suitable use-cases. It’s therefore no surprise that most of the projects I stumbled across during my research which actually made it into production are exactly such games.

How long will it take for WASM to reach MVP and post-MVP status? I don’t know. It’s practically a volunteer project at this point, meaning no deadlines or promises. Just the allusions to it possibly becoming something in between asm.js and NaCl. Almost native code, but not quite native.

I’m more interested at this point to see what other approaches to create the new web-based apps of the (near) future have been dreamed up by people so far. Something which does not involve abusing a flawed scripting language’s runtime to do unspeakable things.

Maya

[1] https://github.com/WebAssembly
[2] https://en.wikipedia.org/wiki/WebAssembly
[3] https://kripken.github.io/emscripten-site/docs/porting/guidelines/portability_guidelines.html
[4] https://kripken.github.io/emscripten-site/docs/porting/guidelines/api_limitations.html
[5] https://kripken.github.io/emscripten-site/docs/porting/guidelines/function_pointer_issues.html
[6] https://kripken.github.io/emscripten-site/index.html
[7] https://kripken.github.io/emscripten-site/docs/porting/emscripten-runtime-environment.html#browser-main-loop
[8] https://github.com/kripken/emscripten/wiki/Asyncify
[9] https://hacks.mozilla.org/2015/12/compiling-to-webassembly-its-happening/
[10] https://github.com/WebAssembly/design/blob/master/PostMVP.md#threads

Advertisements