WebAssembly and asm.js
Native WebAssembly support is expected to hit the stable versions of all four browsers in the first half of 2017, but since there’s no polyfill available (partially because WebAssembly semantics for heap access are different from asm.js), only some big players on the web are expected to deliver native WebAssembly bytecode and a separate asm.js fallback based on browser feature detection; the majority of native code on the web users will likely continue to ship asm.js for the next two to three years (based on experience with other new web platform features).
The asm.js TurboFan compilation pipeline.
The asm.js WebAssembly compilation pipeline.
The WebAssembly compilation pipeline reuses some backend parts of the TurboFan compiler pipeline, and is therefore able to generate
similar code, but with a lot less compilation and optimization overhead, and leveraging various benefits of the WebAssembly
execution environment. For example, the
asm-wasm-builder extracts the asm.js
type annotation and uses those to generate typed WebAssembly code. If it hits any unsupported language constructs, it will just
the whole EcmaScript 2017 language and can only consume a fraction of the static type information. For example consider the
following asm.js module:
It contains two functions
g is an
entrypoint to the asm.js module.
g doesn’t do much but calls
f with whatever input you pass to
g converted to a
Number. According to the asm.js type annotations,
f has type
double -> double, but this information is only usable
as long as the asm.js module validates, i.e. passes the type checks in the asm.js specification.
asm-wasm-builder does the necessary validation and is thus able to generate code that leverages this type information
In this concrete example, the
asm-wasm-builder knows after validation that
f is only used internally in the asm.js module
double and expect
double as result. For example, let’s run the module above through the new
This shows the WebAssembly bytecode generated for the function
f. As you can see the code doesn’t need to perform any
type checks on the input; there’s still a somewhat unnecessary
1.0 in there, but the TurboFan
backend will eliminate this prior to code generation. The final code generated for
f using WebAssembly looks like this:
It first does a so-called stack check, which checks that we don’t exceed a certain stack limit; besides guarding
against stack overflows, this mechanism is used by Chrome to be able to interrupt the main thread of the renderer
process, for example by DevTools, or to kill a tab that is executing an endless loop. The code then computes the square
of the input, which is passed and returned in machine register
xmm1 (WebAssembly has its own native calling convention
that is slightly different from the usual platform calling
conventions). Contrast this with the
scheme, and it
has to perform type checks and conversions on the input
x, and in the end has to allocate a
the resulting value. This translates to a huge improvement in execution speed of bigger applications, which
tend to consist of a lot of small to medium sized functions that are called very often, where having native
calling conventions helps a lot.
For micro-benchmarks that spend most of their time in a single loop or at least a single function, it is
unlikely that you experience a lot of benefits from having asm.js code take the WebAssembly pipeline, because
the TurboFan type inference does a great job at eliminating almost all type checks.
Speed-ups on macro-benchmarks in Massive and Embenchen.
If you are running Chrome dev or canary channel, you can give it a try by enabling the experimental flag Experimental Validate Asm.js and convert to WebAssembly when valid and restarting Chrome.
asm-wasm-builder will log information about asm.js module validation to the Chrome Developer Tools
console, similar to what Firefox does, so you can easily verify that a certain asm.js module
is recognized as valid:
We expect to be able to enable this by default in early 2017 unless we hit some terrible stability or security problems.
Real world performance measurements
Currently this requires quite a bit of knowledge about V8 to really draw useful conclusions from this, but we’re working on better integration with the developer tools in Chrome (i.e. by exposing the stats via lighthouse). Initially it was mostly useful for us internally since we had to understand where todays web pages spent most of their time in V8, but nevertheless you can probably still use some of the data that is gathered. For example you can check how much overall time is spent preparsing your scripts:
We plan to make these measurement tools more accessible during 2017 and provide developers with better insight into V8 performance. We also plan to continue our effort to focus on real world performance improvements rather than looking at benchmarks only, which was the main driver in the past.
Apparently there’s a slide deck from my colleagues Camillo Bruni from the V8 runtime team and Michael Lippautz from the V8 GC team that describes how to get to the Runtime Call Stats and the Heap Stats in chrome://tracing.
It should give you a rough idea how to use the new functionality in Chrome.