Abstract: WebAssembly has a future.

  • How does JavaScript work: Compare to WebAssembly and its usage scenarios
  • Author: Front-end xiaozhi

FundebugReproduced with authorization, copyright belongs to the original author.

This is the sixth article in a series devoted to exploring JavaScript and the components it builds.

If you missed the previous chapters, they can be found here:

  • How JavaScript works: An overview of the engine, runtime, and call stack!
  • How JavaScript works: Get deep into the V8 Engine & 5 Tips for Writing Optimized Code
  • How JavaScript works: Memory Management + How to handle 4 common memory leaks
  • How JavaScript works: Event loops and the rise of asynchronous programming + 5 ways to code better with async/await!
  • How JavaScript works: Explore websocket and HTTP/2 with SSE + how to choose the right path!

This time I’ll show you how WebAssembly works and, more importantly, how it compares to JavaScript in terms of performance: load time, execution speed, garbage collection, memory usage, API open platform, debugging, multithreading, and portability.

First, what does WebAssembly do

First, it’s important to take a look at ASM.js. In 2012, Mozilla engineer Alon Zakai was working on the LLVM compiler when he had an idea: A lot of 3D games are written in C/C++. If you could compile C/C++ into JavaScript code, wouldn’t they run in a browser? It is well known that the basic syntax of JavaScript is highly similar to that of C. So he began to research how to achieve this goal, creating a compiler project called Emscripten. This compiler can compile C/C++ code into JS code, but not regular JS, but a variant of JavaScript called ASm.js, with almost 50% performance of native code.

Then Google developed the Portable Native Client, a technology that lets browsers run C/C++ code. Later, perhaps because they shared a higher goal, Google, Microsoft, Mozilla, Apple and several other large companies teamed up to create a universal binary and text format for the Web called WebAssembly. Asm.js and WebAssembly function basically the same, except that the code is different: ASM.js is text, WebAssembly is binary bytecode, so it is faster and smaller.

WebAssembly(also known as WASM) is a new bytecode format that is already supported by major browsers. Unlike JS interpretation execution, WebAssembly bytecode is similar to the underlying machine code and can be loaded and run quickly, thus greatly improving performance compared to JS interpretation execution. In other words, WebAssembly is not a programming language, but a bytecode standard. It needs to be compiled in a high-level programming language and put into a WebAssembly VIRTUAL machine to run. Browser manufacturers need to implement the virtual machine according to the WebAssembly specification.

WebAssembly load time

WebAssembly loads faster in a browser because only wASM files that are already compiled need to be transferred over the Internet. Wasm is a low-level assembly language with a very compact binary format.

WebAssembly execution speed

Wasm now runs only 20% slower than native code, which is a pleasant surprise. It is a format that is compiled into a sandbox environment and runs under a number of constraints to ensure that there are no security holes or enhancements. The drop in execution speed is minimal compared to true native code. More importantly, the future will be even faster.

Even better, it’s browser-agnostic — all the major engines have added WebAssembly support, and execution speeds are about the same.

To understand how fast WebAssembly executes compared to JavaScript, you should first read about how the JavaScript engine works.

Let’s take a quick look at how V8 works:

On the left, there is some JavaScript source code that contains JavaScript functions. You first need to parse it to convert all strings into tags and generate an abstract syntax tree (AST). An AST is an in-memory representation of the logical structure of a JavaScript program. Once the AST is generated, V8 goes straight to the machine code stage. After traversing the tree to generate machine code, the compiled function is obtained without increasing the traversal speed in the process.

Now, let’s look at the next phase of the V8 pipeline:

Now with V8’s new TurboFan optimized compiler, a lot of code runs in V8 when JavaScript applications are running. TurboFan monitors for code that is running slowly, performance bottlenecks and hot spots (places where memory usage is too high) so it can be optimized. It pushes the monitored code to the back end, an optimized just-in-time compiler that converts cpu-intensive functions into better-performing code.

It solves the performance problem, but there is a downside to this approach. The process of analyzing code and deciding what to optimize can also consume CPU, which means higher power consumption, especially on mobile devices.

However, WASM does not require all of the above steps – it is inserted into the execution schematic shown below:

At compile time, WebAssembly does not need to be transformed because it is already bytecode. Anyway, the above parsing is no longer required, and you have optimized binaries that can be plugged directly into the back end (just-in-time compiler) and generate machine code. The compiler has done all the code optimization work on the front end.

This makes wASM execution more efficient by skipping many steps in the compilation process.

WebAssembly memory model

For example, the memory of a c++ program compiled into WebAssembly is a contiguous block of memory with no “holes.” One feature of WASM that helps improve security is the concept of implementing stack separation from linear memory. In c++ programs, if you have a heap, allocate from the bottom of the heap, and then get memory from the top to increase the size of the memory stack. You can get a pointer and walk through stack memory to manipulate variables that you shouldn’t touch.

This is the vulnerability that most suspect software can exploit.

WebAssembly follows a completely different internal model. The execution stack is separate from the WebAssembly program itself, so there is no way to modify or change values such as variables. Again, these functions use integer offsets instead of Pointers. The function points to a table of indirect functions. These directly computed numbers are then fed into the module’s functions. Built this way, you can load multiple WASM modules at the same time, offset all indexes, and each module works fine.

More articles on JavaScript memory models and management can be found here.

WebAssembly garbage collection

In JavaScript, developers don’t have to worry about garbage collection in memory. The JS engine uses something called the garbage collector to do the garbage collection automatically.

Right now, WebAssembly does not support garbage collection at all. Memory is managed manually (just like C/C++). While this may make programming more difficult for developers, it does improve performance.

Currently, WebAssembly is designed specifically around usage scenarios for C++ and RUST. Because WASM is a very low-level language, this means that programming languages that are only one level above assembly language can easily be compiled into WebAssembly. C can use malloc, C++ can use smart Pointers, and Rust uses a completely different schema (an entirely different topic). These languages don’t use a memory garbage collector, so they don’t need all that complicated run-time stuff to keep track of memory. WebAssembly is a natural fit for these languages.

In addition, these languages are not 100% suitable for complex JavaScript usage scenarios such as listening for DOM changes. It makes no sense to write an entire HTML program in C++ because C++ wasn’t designed for that. In most cases, engineers write WebGL or highly optimized libraries (such as lots of math) using C++ or Rust.

However, in the future WebAssembly will support languages with no memory garbage return.

WebAssembly platform interface access

Depending on the runtime environment that executes JavaScript, access to platform-specific apis is exposed, and specific interfaces exposed by these platforms can be accessed directly through JavaScript programs. For example, if you are running JavaScript in a browser, there is a set of Web apis that a Web application can call to control Web browser/device functionality and access DOM, CSSOM, WebGL, IndexedDB, Web Audio API, and so on.

However, WebAssembly modules do not have access to any platform APIS. All of this has to be mediated by JavaScript. If you want to access some platform-specific API in a WebAssembly module, you must call it through JavaScript.

For example, if you want to use console.log, you have to call it in JavaScript, not C++ code. These JavaScript calls incur a performance penalty.

Things won’t stay the same. The spec will provide an interface for WASM to access specific platforms in the future so that you don’t have to build JavaScript into your applications.

Start with source code conversion

JavaScript scripts are becoming more complex. Much of the source code, especially the various libraries and frameworks, has to be transformed before it can be put into production.

Common source code conversion, mainly the following three cases:

  1. Compress, reduce the volume. For example, the source code of jQuery 1.9 is 252KB before compression and 32KB after compression.
  2. Multiple files are merged to reduce the number of HTTP requests.
  3. Other languages compile to JavaScript. The most common example is CoffeeScript.

In all three cases, the actual running code is different from the development code, and debugging becomes difficult. If you want to improve debugging efficiency, welcome to try Fundebug for free.

Usually, the JavaScript interpreter will tell you which lines and columns of code are wrong. However, this is useless for the transformed code. For example, jQuery 1.9 is compressed to just three lines of 30,000 characters each, with all internal variables renamed. You look at the error message and feel clueless about its original location.

This is the problem Source Map is trying to solve.

Source map

Simply put, a Source Map is an information file that stores location information. That is, every position in the transformed code corresponds to the position before the transformation. With this, when something goes wrong, the debugger will display the original code instead of the converted code. This undoubtedly brings great convenience to developers.

The Source map is not currently supported by WebAssembly because there is no specification for it, but it will be (probably soon). When you set breakpoints in C++ code, you will see C++ code instead of WebAssembly. At least, that’s the goal of the WebAssembly source map.

Read the Source Map primer for more details.

multithreading

JavaScript is single-threaded. There are ways to take advantage of event loops and take advantage of asynchronous programming, and this was previously covered in how JavaScript works: the rise of event loops and asynchronous programming + 5 ways to code better with async/await.

JavaScript also uses Web Workers, but only in very special cases – in general, any cpu-intensive computation that might clog the UI main thread can be handed over to the Web Worker for better performance. However, the Web Worker cannot access the DOM.

Currently WebAssembly does not support multithreading. However, this is probably what WebAssembly will do next. Wasm will be close to implementing native threads (i.e., C++ style threads). Having real threads will open up a lot of new opportunities in browsers. And, of course, increases the potential for abuse.

portability

JavaScript can now run almost anywhere, from the browser to the server, and even in embedded systems.

WebAssembly is designed to be secure and portable. Like JavaScript. It will run in every WASM-enabled environment (for example, every browser).

WebAssembly has the same goals for portability that Java used Applets in the early years.

WebAssembly usage scenarios

The original version of WebAssembly was designed to solve a large number of computationally intensive computations (such as math problems). The most popular application scenario is games — with lots of pixels. You can write C++/Rust programs using the OpenGL bindings you are familiar with and compile them into wasm. After that, it runs in a browser.

In the browser

  • Better yet, some languages and tools can be compiled to run on the Web platform.
  • Photo/video editing.
  • Game:
    • Small games that need to be opened quickly
    • AAA, with a lot of resources.
    • Game Portal (agency/Original game platform)
  • P2P applications (games, real-time co-editing)
  • Music player (streaming, caching)
  • Image recognition
  • Live video
  • VR and virtual reality
  • CAD software
  • Scientific visualization and simulation
  • Interactive educational software and news articles.
  • Simulation/simulation platform (ARC, DOSBox, QEMU, MAME…) .
  • Language compiler/virtual machine.
  • POSIX user-space environment that allows porting of existing POSIX applications.
  • Developer tools (editors, compilers, debuggers…)
  • Remote desktop.
  • The VPN.
  • Encryption tools.
  • Local Web server.
  • Plug-ins distributed using NPAPI, but limited by Web security protocols, can use Web APIs.
  • Enterprise software functional clients (e.g., databases)

Out of the browser

  • Game distribution services (portable, secure).
  • The server executes untrusted code.
  • Server applications.
  • Mobile hybrid native apps.
  • Multi-node symmetric computing

The original:blog.sessionstack.com…

The possible bugs in editing cannot be known in real time. In order to solve these bugs after the event, I spent a lot of time on log debugging. Here incidentally, I recommend a good bug monitoring tool Fundebug.

Your likes are my motivation to keep sharing good things.

A stupid code farmers, my world can only lifelong learning!

More content please pay attention to the public account “big move the world”!

About Fundebug

Fundebug focuses on JavaScript, wechat applets, wechat mini games, Alipay applets, React Native, Node.js and Java real-time BUG monitoring. Since its official launch on November 11, 2016, Fundebug has handled more than 900 million error events in total, which has been recognized by many well-known users such as Google, 360, Kingsoft, and People’s Net. Welcome free trial!