使用SIMD提升JavaScript应用性能
-胡宁馨,韩盼
1. 背景介绍
现代微处理器利用并行运算在有限的计算资源下提供高性能的处理能力。在一个应用程序中通过单指令多数据( SIMD)指令,处理器细粒度并行处理多个数据项,给应用程序带来了重大的性能改进。 SIMD指令特别适用于图像/音频/视频的处理计算,例如计算机视觉和感知计算。由于其卓越的能效和出色的性能提升, SIMD在现代系统中的作用越来越大。然而到现在为止,JavaScript缺少对SIMD的支持,这样广大的web应用开发者无法在自己的应用中利用SIMD来提升自己应用的性能。随着HTML5的标准化,相信有越来越多的复杂的web应用会随之诞生,同时对性能的需求也会越来越高。SIMD.js是对原有JavaScript的一个扩展使其支持SIMD编程。本文就意在介绍在现有的JavaScript引擎上如何使用SIMD.js编程,并且会利用一些具体的例子来体现性能的提升。
通过与Mozilla和Google的合作,Intel将SIMD.js语法规范化。通过使用我们建议的SIMD数字类型,现在可以利用纯的JavaScript代码开发出全新类别的计算密集型应用,例如游戏,图像/视频处理软件,而不需要额外的插件或不可移植的原生代码。
2. SIMD
* 什么是SIMD
正如上面提到, SIMD代表的是单指令,多数据。 SIMD利用数据的并行性同时对多个数据进行相同的操作。今天,大量的处理器具有SIMD操作指令。 SIMD运算可以大大受益于如3D图形和音频/视频处理中的多媒体应用领域的应用。
* SIMD编程
一个SIMD值有多个分量。在下面这个例子中,我们使用的是两个长度为4的SIMD向量,每个分量被命名的x,y ,z和w。下图显示了一个SIMD加法运算。不需要对4个独立的分量执行4次加法运算, SIMD允许同时对四个分量一齐进行加操作。因为采用较少的操作次数,所以大大提高了新能并且节省了电能。
3. SIMD在JavaScript中的使用
* 数据类型
SIMD.js在现有的JavaScript中增加了新的数据类型,如下表:
float32x4 | 4 IEEE-754 32-bit Floating Point Numbers |
int32x4 | 4 32-bit Signed Integers |
float64x2 | 2 IEEE-754 64-bit Floating Point Numbers |
Float32x4Array | Typed Array of float32x4 |
Int32x4Array | Typed Array of int32x4 |
Float64x2Array | Typed Array of float64x2 |
这些数据类型在使用的过程中有一些要注意的地方。在JavaScript中SIMD的数据类型是不可变的,这意味着不能直接改变一个SIMD的实例。尽管一个SIMD实例中有4个不同值的分量,但是却不能把它当做一个链表来使用。正确的做法应当是在不同的SIMD数据之间进行操作进而产生新的SIMD数据。下文就介绍一些常用的SIMD操作。
- 操作
Constructor
var a = SIMD.float32x4(1.0, 2.0, 3.0, 4.0);
1.0 | 2.0 | 3.0 | 4.0 |
var b = SIMD.float32x4.zero();
0.0 | 0.0 | 0.0 | 0.0 |
var c = SIMD.float32x4.splat(1.0);
1.0 | 1.0 | 1.0 | 1.0 |
Access and Modify
var a = SIMD.float32x4(1.0, 2.0, 3.0, 4.0);
1.0 | 2.0 | 3.0 | 4.0 |
var b = a.x; // 取得a中第一个分量的值,其他依次为y,z,w.
var c = a.withX(5.0); //改变第一个分量的值,形成一个新的SIMD数据
5.0 | 2.0 | 3.0 | 4.0 |
Arithmetic
var a = SIMD.float32x4(1.0, 2.0, 3.0, 4.0);
var b = SIMD.float32x4(5.0, 10.0, 15.0, 20.0);
var c = SIMD.float32x4.add(a,b);
1.0 | 2.0 | 3.0 | 4.0 |
+
5.0 | 10.0 | 15.0 | 20.0 |
=
6.0 | 12.0 | 18.0 | 24.0 |
Shuffle
var a = SIMD.float32x4(1.0, 2.0, 3.0, 4.0);
1.0 | 2.0 | 3.0 | 4.0 |
var b = SIMD.float32x4.shuffle(a, SIMD.float32x4.XXYY);
1.0 | 1.0 | 2.0 | 2.0 |
var c = SIMD.float32x4.shuffle(a, SIMD.float32x4.WWWW);
4.0 | 4.0 | 4.0 | 4.0 |
var d = SIMD.float32x4.shuffle(a, SIMD.float32x4.WZYX);
4.0 | 3.0 | 2.0 | 1.0 |
Compare
var a = SIMD.float32x4(1.0, 2.0, 3.0, 4.0);
1.0 | 2.0 | 3.0 | 4.0 |
var b = SIMD.float32x4(0.0, 3.0, 5.0, 2.0);
0.0 | 3.0 | 5.0 | 2.0 |
vargreaterThan = SIMD.float32x4.greaterThan(a, b);
0xF | 0x0 | 0x0 | 0xF |
Example
求平均数:
function average(list) {
var n = list.length;
var sum = 0.0;
for (inti = 0; i< n; i++) {
sum += list[i];
}
return sum / n;
}
SIMD写法:
function average(f32x4list) {
var n = f32x4list.length;
var sum = SIMD.float32x4.zero();
for (inti = 0; i< n; i++) {
sum = SIMD.float32x4.add(sum, f32x4list.getAt(i));
}
var total = sum.x + sum.y + sum.z + sum.w;
return total / (n * 4);
}
总的来说,SIMD.js中的数据类型和操作可以概括为下图:
完整的API可以参考John的polyfill实现:https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js
{C}{C}{C}{C}{C}{C}{C}{C}· 性能分析
下图展示了在一些基本JavaScript运算benchmark上的性能提升,红线代表的是4倍性能提升,4倍是理论上的使用向量宽度为4的SIMD数据类型的加速比。
以下两张图展示一个简单的小鸟图形示例,分别在开启SIMD和关闭SIMD的情况下小鸟的个数有明显的不同。
SIMD.js可以和WebWorker整合使用会得到更好的性能提升。以下四张图分别展示了在没有开启SIMD,开启SIMD,开启SIMD并且两个WebWorker同时渲染,开启SIMD并且三个WebWorker同时渲染的效果图。
4. SIMD.js 在JavaScript引擎中的实现
2014年8月, JavaScript标准化组织TC39已经批准了SIMD.js成为第一阶段的标准提案。与此同时,SIMD.js已经实现在Mozilla的Firefox Nightly浏览器和基于Chromium浏览器的原型中,微软的Internet Explorer浏览器也正在考虑实现SIMD.js。
SIMD.js API的设计考虑了JavaScript引擎实现的效率。优化过的JavaScript引擎可以将SIMD.js API的调用最终编译为底层硬件的支持的SIMD指令。比如,基于Intel的平台,JavaScript引擎会将SIMD.float32x4.add编译为addps指令,将SIMD.float32x4.mul编译为mulps指令。相应的,针对其他硬件平台,JavaScript也可以生成对应的指令。这些指令可以同时对四个浮点数进行加法或则乘法操作,从而达到提升性能和节省电能的效果。
5. SIMD.js 重要性
随着HTML5应用的普及,越来越多的web开发者会利用新的技术开发越来越复杂的web应用,而SIMD技术的应用会使这些web应用的性能大大提高。例如一些 WebGL,Canvas,Games,Physics,Crypto等都是典型的可以利用SIMD加速的应用。
另外SIMD代码的执行只需要相对少数的指令,相对少数的指令意味对电能也是一种节省。
6. References
- SIMD in JavaScript :https://01.org/zh/node/1495?langredirect=1
- Bringing SIMD to JavaScript: https://01.org/blogs/tlcounts/2014/bringing-simd-javascript
- EcmaScript strawman proposal: http://wiki.ecmascript.org/doku.php?id=strawman:simd_number
- John McCutchan’s SIMD in JavaScript polyfill source: https://github.com/johnmccutchan/ecmascript_simd
- Peter Jensen’s Mandelbrot source: https://github.com/PeterJensen/mandelbrot
- JavaScript SIMD benchmarks source: https://github.com/johnmccutchan/ecmascript_simd/tree/master/src/benchmarks
- JavaScript gains support for SIMD. Blog post by Dr. Axel Rauschmayer: http://www.2ality.com/2013/12/simd-js.html