2013-12-28

JavaScript gains support for SIMD

Recently, a new JavaScript feature has landed for the next Firefox Nightly: an API for SIMD (Single Instruction, Multiple Data). This blog post explains how the API works and how it fits into the JavaScript landscape.

What is SIMD?

SIMD is the ability of a CPU to apply an operand to vectors of values, instead of single values. For example, a CPU may be able to add two vectors v and w by adding their components, hence applying the single instruction addition to multiple data:
v + w = 〈v1, …, vn〉+ 〈w1, …, wn
= 〈v1+w1, …, vn+wn
SIMD brings speed-ups to many kinds of number crunching (3D computations, image processing, singal processing, etc.).

John McCutchan (Google) and Peter Jensen (Intel) have proposed a JavaScript API for SIMD. That API initially grew out of the SIMD support in Dart, because Dart SIMD code needed to be compiled to JavaScript.

At the moment, the API provides two data types:

  • float32x4 (C type: __m128): four 32 bit floating point numbers.
  • uint32x4 (C type: __m128i): four 32 bit unsigned integers.
You use these types as follows: You first create members of the types and then apply an operator.
    var a = SIMD.float32x4(1.0, 2.0, 3.0, 4.0);
    var b = SIMD.float32x4(5.0, 6.0, 7.0, 8.0);
    var c = SIMD.float32x4.add(a,b);
A few examples of available operators:

float32x4.abs(v)absolute values of v
float32x4.neg(v)negated values of v
float32x4.sqrt(v)the square roots of the values of v
float32x4.add(v, w)pairwise addition of v and w
float32x4.mul(v, w)pairwise multiplication of v and w
float32x4.equal(v, w)determines, per pair of values from v and w, whether they are equal and returns the resulting booleans as a uint32x4

Apart from the aforementioned native implementation in Firefox, the API has also been implemented in vanilla JavaScript, based on typed arrays. The source of the latter is well documented, so you can use it look up all operators that the API supports.

How does the SIMD API fit into the JavaScript landscape?

The goal is to translate most of the SIMD operations directly to CPU commands (after intermediate steps). Given how specific the types are, you may be wondering why a JavaScript feature is so closely tied to CPU instructions. The answer is that there are two use cases for data parallelism (of which SIMD is an example) in JavaScript:
  1. Low-level data parallelism primitives for hand-optimized code and for compiling to JavaScript.
  2. Support for generic data parallelism that works across a wide variety of processor architectures.
Looking at use case 1, it is obvious that TC39 [1] meant it when they stated the following goal for upcoming versions of JavaScript:
Be a better language for writing code generators targeting [ECMAScript Harmony].
The SIMD API is already supported by Emscripten, enabling it to compile SIMD code written in C to JavaScript. You can expect asm.js [2] to support the API, too.

What about the second use case? Here, ParallelJS [3] shines. It isn’t tied to specific vector sizes or vector element sizes. And it will probably eventually even be able to produce GPU code.

Lastly, I find it interesting how strongly Intel is interested in adding SIMD support to JavaScript. They helped Mozilla’s Niko Matsakis with implementing SIMD for Firefox and they are reportedly doing the same for Google’s V8 JavaScript engine. One reason is probably that they want JavaScript to run well on their mobile operating system Tizen.

The SIMD API won’t be in ECMAScript 6, but will probably be included in either ECMAScript 7 or ECMAScript 8.

Further reading

References

  1. A JavaScript glossary: ECMAScript, TC39, etc.
  2. asm.js: closing the gap between JavaScript and native
  3. ParallelJS: data parallelism for JavaScript

No comments: