Alexander Zlatkov translation: Troland
github.com/Troland/how-javascript-works/blob/master/webassembly.md
This is chapter 6 of how JavaScript works.
Now, we’ll take a look at how WebAssembly works and, most importantly, how it compares to JavaScript in terms of performance: load time, execution speed, garbage collection, memory usage, platform API access, debugging, multithreading, and portability.
The way we build web applications is undergoing a revolution – this is just the beginning and the way we think about web applications is changing.
First, get to know WebAssembly
WebAssembly (also known as WASM) is an efficient, low-level bytecode for developing web applications.
WASM lets you write applications in languages other than JavaScript (such as C, C++, Rust, and others) and then compile them into (early) WebAssembly.
The resulting web application will be very fast to load and run.
Loading time
In order to load JavaScript, the browser must load all text-formatted JS files.
Browsers load WebAssembly more quickly because WebAssembly only transfers wASM files that are already compiled. And WASM is an underlying assembler like language with a very compact binary format.
Execution speed
Today, Wasm runs only 20% slower than native code. In any case, this is a pleasant result. 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. In addition, the future will be much faster.
Even better, it has good browser-compatible features – all the major browser engines support WebAssembly and run at similar speeds.
To understand how fast WebAssembly performs compared to JavaScript, you should first read previous articles on how JavaScript engines work.
Let’s take a quick look at how V8 works:
On the left is the JavaScript source code, which contains JavaScript functions. First, the source code converts the string to tokens for parsing, and then generates a syntax abstraction tree.
Syntax abstraction trees are in-memory representations of the logic of your JavaScript program. Once the diagram is generated, V8 goes straight to the machine code stage. You basically walk through the tree, generate the machine code and get the compiled function. There is no real attempt to speed up the process.
Now, let’s take a look at the next phase of the V8 pipeline:
Now we have TurboFan, one of V8’s optimized compilers. When JavaScript is running, a lot of the code is running inside V8. TurboFan monitors code that is running slowly, places that are causing performance bottlenecks, and hot spots (places where memory usage is too high) to optimize them. 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 the downside is that the process of analyzing code and identifying which code needs to be optimized can also consume CPU resources. That means more power consumption, especially on mobile devices.
However, WASM does not require all of the above steps – it is inserted into the execution process as follows:
Wasm goes through code optimization at compile time. Anyway, parsing is not needed either. You have optimized binaries that plug 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.
The memory model
For example, the memory of a C++ program is compiled into a WebAssembly, which is a contiguous chunk of memory with no holes. One feature in WASAM that can be used to improve code security is the concept of execution stack and linear memory isolation. In C++ programs, you have an area of dynamic memory, and you get the memory stack from the bottom of it, and then you 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 takes a completely different memory model. The execution stack is isolated from the WebAssembly program itself, so you can’t modify or change things like variable values from within. Similarly, 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. This is how it works, so you can introduce 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.
Memory garbage collection
You already know that JavaScript memory management is handled by the memory garbage collector.
Things are a little different for WebAssembly. It supports languages that manipulate memory manually. You can also build a memory garbage collector into your WASM module, but this is a complex task.
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.
Platform interface access
Depending on the runtime environment that executes JavaScript, specific interfaces exposed by these platforms can be accessed directly through JavaScript programs. For example, when you run JavaScript in a browser, a Web application can call a series of Web interfaces to control browser/device functionality and access DOM, CSSOM, WebGL, IndexedDB, Web Audio APIS, and more.
However, a WebAssembly module cannot access any platform’s interface. All of this has to be coordinated by JavaScript. If you want to access some platform-specific interface from within a WebAssembly module, you must do so through JavaScript.
For example, if you want to use console.log, you have to do it in JavaScript instead of 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.
Source mapping
When you compress JavaScript code, you need to have a proper way to debug it.
This is where source mapping comes in handy.
In essence, source mapping is a way of mapping merged/compressed files to an unbuilt state. When you build code for a production environment, along with compressing and merging JavaScript, you generate source maps to store the original file information. When you want to query the generated JavaScript code for specific rows and columns, you can look in the source map to return the original location of the code.
There is no specification defining source mapping, so it is not currently supported by WebAssembly, 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.
multithreading
JavaScript is single-threaded. There are many ways to take advantage of event loops and use asynchronous programming as mentioned in previous articles.
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 browsers to servers and even embedded systems.
WebAssembly is designed for security and portability. Just like JavaScript. It will run in any environment that supports WASM, such as 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 usage scenario is games — dealing with large numbers 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.
Browsing the (run) in fire isolated – http://s3.amazonaws.com/mozilla-games/tmp/2017-02-21-SunTemple/SunTemple.html. This is running in the Unreal Engine, which is a development kit that can be used to develop virtual reality.
Another sensible use of WebAssembly (high performance) is to implement libraries that handle computationally intensive processing. For example, some graphical operations.
As mentioned earlier, WASM can effectively reduce power consumption on mobile devices (engine dependent) because most of the steps are handled ahead of time at compile time.
In the future, you’ll be able to use the WASM binary library even if you haven’t written the code to compile it. You can find some projects that are starting to use this technology at NPM.
For DOM manipulation and frequent use of platform interfaces, it makes more sense to use JavaScript because it incurs no additional performance overhead and it supports various interfaces natively.
At SessionStack we are committed to continuously improving JavaScript performance to write high quality and efficient code. Our solution must have lightning-fast performance because we cannot affect the performance of the user application. Once you integrate SessionStack into the production environment of your web application or website, it starts logging everything: all DOM changes, user interactions, JavaScript exceptions, stack traces, failed web requests, and debug data. All of this happens in your production environment without affecting any interaction or performance of your product. We must greatly optimize our code and make it execute asynchronously as much as possible.
We don’t just have libraries, we have other features! When you replay the user session in the SessionStack, we have to render everything that happened to your user’s browser when the problem occurred, and we have to reconstruct the entire state to allow you to jump back and forth across the session timeline. To make this possible, we use asynchronous operations a lot, because there is no better alternative in JavaScript.
With WebAssembly, we can hand over a lot of data computation and rendering to more appropriate languages and leave data collection and DOM manipulation to JavaScript.
Set pieces
Open webAssembly’s official website and you’ll see the browser that displays its compatibility prominently in the header. It’s Fire Orphan, Chrome, Safari, IE Edge. Click on Learn More to see that this is a preview browser release agreed on 2/28, 2017. It is now being implemented and will be ready for production at some point in the future. A subset of JavaScript called ASm.js is introduced on the website. In addition, there is a WebAssembly and JavaScript performance comparison test site.
Find this article helpful? Please share it with more people