I was just reflecting on yesterday's hack of my 81 hacks, considering the possibility of adding an asm.js code generator to it: http://canonical.org/~kragen/sw/81hacks/oscarray/notes. Being able to run that code in Chromium as well as Iceweasel would be a big benefit! Especially if Chromium adds SIMD.js as well (currently only in Firefox nightly; see https://blog.mozilla.org/javascript/2015/03/10/state-of-simd... for more details) since that would enable JS code to generate code at runtime that would get performance several times better than optimized C, at least for the kinds of graphics, DSP, and machine learning problems that are easy to implement in SIMD.
SIMD.js still looks pretty half-baked (you can't supply vector arguments to provide the indexes for shuffling? Really?) but already promising.
Most SIMD hardware instruction sets don't support (dynamic) vector arguments to provide the indices for shuffling, so it wouldn't be fast if SIMD.js' API had it.
I'd argue most SIMD hardware instruction sets do support that. x86 has had pshufb since SSSE3 (most desktop+laptop, and ~all mobile x86), providing an arbitrary byte shuffle across 16 bytes, and NEON has vtbl, pretty much the same but limited to 8 bytes of output per instruction.
Now, I will admit that those instructions are not always a good idea (particularly on mobile x86, where pshufb is often several cycles), and they're essentially never a good idea when a specialized instruction (e.g. punpcklbw, vtrn) can do the job.
For SIMD types with elements other than Int8, in addition to the pshufb, there's also the cost of computing the pshufb byte indices.
For additional context, the current version of SIMD.js is aimed at covering the basics that strike some balance of being fast on most hardware and being useful. It's also just the beginning, and we expect it'll evolve to add many more features, quite likely including pshufb-like functionality.
It's not just SSE2; SIMD.js also includes things like Int32x4.mul, which is a little tricky without SSE4.1's pmulld. It's kind of a balancing act between several concerns. Also, it's a base, and we definitely plan to iterate and add more features on top of it.
I guess you probably posted this as some sort of joke, but people seem to be taking you seriously, so I am going to take the bait and respond seriously.
I am really at a loss as to how the current SIMD.js API for shuffles could possibly be fast, or how using vector arguments to specify the permutation would make it slower.
No hardware instruction set has an instruction that takes 10 or 18 separate operands the way the SIMD.js API does. NEON has VTBL and VTBX, SSE2 doesn't have shuffling instructions at all (unless you count the rather pathetic SHUFPD, which packs two double-precision values into its output), and SSSE3 adds PSHUFB. All of them use vector arguments to provide the indices for shuffling.
To compile a SIMD.Int8x16.shuffle call into an efficient PSHUFB, the compiler is going to have to figure out whether the s15:0 arguments are drawn from an existing vector and additionally that they all necessarily index into only one of the vector arguments. VTBL is more powerful, but it still needs the indices to be in a vector. So if the compiler isn't able to figure out that you were extracting the indices from a vector, it has to reconstitute that vector in order to invoke VTBL.
SIMD.js's shuffle instructions closely resemble LLVM's shuffle instructions, and LLVM also require shuffle indices to be constant, so this isn't restriction isn't unique to SIMD.js. Also, x86's shuffle situation is much more involved than your post here acknowledges; it includes shufps, shufpd, movlhps, movlhpd, movhlps, movhlpd, unpcklps, unpcklpd, unpckhps, unpckhpd, movss, movsd, and more, and all of these are effectively constant shuffle masks.
I agree that there's more we can do here, and there are some interesting questions if we want to abstract over pshufb and VTBL and so on. Fortunately, SIMD.js is an evolving API and we can add things to it.
Oh, I didn't realize SIMD.js required the shuffle indices to be constant in order to generate efficient code. In that case, sure, you can generate efficient code and take advantage of the panoply of special-purpose shunting instructions you mention, and writing and reading the SIMD.js code is even going to be easier than SSE.
Exactly. In general, SIMD.js intentionally implements only vector operations that translate nicely to hardware, not all potentially useful vector operations. Then, when used together with asm.js, browsers can translate it directly to hardware vector instructions.
Though, it does seem like anyTrue() and allTrue() are pretty non trivial on ARM with NEON. Those functions seem pretty geared towards x86 movemask/ptest instructions. I'd think those would have to be compound, if not serial, on ARM.
You're right. There's enough diversity in SIMD instruction sets that no single rule seems sufficient for deciding what to include. Operations which are "fast" on all popular hardware are obviously great, but SIMD.js also includes some operations needed by popular use cases, such as allTrue() and anyTrue(). Also, while these operations are "fast" on x86 as you say, on NEON they're at least no worse than what applications would do otherwise.
I think SIMD.js need more operations, which may not directly map to hardware, but are "at least no worse than what applications would do otherwise":
1. The most obvious operation is multiply-accumulate, which would map to either multiply + add instructions, multiply-add instruction (with intermediate rounding), or FMA instruction. For a linear algebra (BLAS) library, it would be a one-minute fix to make use of this operation, and it would double performance on modern CPUs.
2. Another kind of operations I would like to see is load-and-deinterleave/store-and-interleave, e.g. operation which loads 12 floats of interleaved RGB data and returns 3 Float32x4 vectors with red, green, blue components. These operations map to a single instruction on ARM, and map to a nice sequence of instruction on x86 with SSSE3, and to a much longer sequence of instructions on x86 with SSE2. The best a developer can do with the current SIMD.js API is the analog of very suboptimal SSE2 code.
3. Operations which extend the type, e.g. load 4 16-bit signed integers and extend them to 4x32 vector. The optimal sequence is:
- PMOVSXWD xmm, [mem] on x86 with SSE 4.1
- MOVQ xmm, [mem] + PXOR xmm2, xmm2 + PCMPGTB xmm2, xmm + PUNPCKLBW xmm, xmm2 on x86 with SSE2
- VLD1.16 dTemp, [rAddr] + VMOVL.S16 qOut, dTemp on ARMv7.
The best the developer can do with SIMD.js now is SSE2 approach.
If you are interested in these ideas, I have more operations in mind that would be useful.
FMA is definitely something we want to add. The question is what to do when hardware doesn't have FMA, as it's quite expensive to compute manually. Some applications would want to fall back to discrete multiply and add, but of course that rounds differently and other applications don't want implicit rounding differences between platforms. I believe the solution is to give applications a say in what happens, though the details are still being discussed.
The load-and-deinterleave/store-and-interleave operations are good ideas too. They're a little more complex than we could accommodate the initial version of SIMD.js, but they're definitely things we should consider.
Working with 8-bit and 16-bit data is an area where the current version of SIMD.js is fairly limited overall. Extending loads are a good idea, and in general I'm hoping a future version of SIMD.js will provide a much more complete set of operations.
I'm interested in any other ideas you have as well. SIMD.js is being developed at https://github.com/tc39/ecmascript_simd and you're welcome to file issues to send us your ideas. Thanks!
Yes, and it would be interesting to compare WASM with the native Node plugins performance-wise. Especially looking at how it handles the interaction between JS and plugins/wasm. AFAIK, right now there are penalties associated with moving values in and out of V8 when using native plugins. If WASM can avoid that, native plugins may switch to it.
Is this DOM/UI-less ? Everything I've read on WASM so far has indicated that it would be (initially) similar to web workers and not give access to the DOM.
Would you please clarify what problems are you referring at? Some Java-in-the-browser problems that seem orthogonal to the minutia of the language running in the browser:
* Language-based security model. Hopefully not an issue, browsers have been pretty good at sticking with same origin policy and not trying to mix code from different providers in the same address space.
* sun.misc.unsafe. As long as the APIs available to WebAssembly are the same as the Javascript APIs, not an issue.
* Slow startup times. For a decent loader / JIT infrastructure this is not an issue regardless of the language. In other words, an implementation detail and not a systemic issue.
* AWT / Java2D. There is a chance non-DOM widget toolkits will make a comeback, but that's less of a WebAssembly issue and more of a Canvas issue. Canvas has been around for a while, but most people still use the DOM.
"programs which invoke undefined behavior at the source language level may be compiled into WebAssembly programs which do anything else, including corrupting the contents of the application heap, calling APIs with arbitrary parameters, hanging, trapping, or consuming arbitrary amounts of resources (within the limits)." [1]
So now it's possible to hammer on the entire JavaScript API with invalid parameters. How long before someone finds a hole?
That quote is talking about (untrusted) compilers that translate from C/C++ with undefined behavior to WebAssembly programs. The latter would still have a defined behavior.
People already "hammer on the entire JavaScript API with invalid parameters". That's what fuzzing is, and yes, it does find implementation errors which can lead to security problems.
SIMD.js still looks pretty half-baked (you can't supply vector arguments to provide the indexes for shuffling? Really?) but already promising.