前言
在一次面试中被安利了WebAssembly。于是自己写了几个代码,测试一下业务场景中最常用到的四则运算,WebAssembly能提高多少性能。并非专业测试,环境为:A8-7500(四核3GHz)+8G内存,win7系统,Chrome
62。所有测试会用同样的代码跑10次,因为每次结果非常接近,这里只给出最终的平均值。过程不保证完全严谨,结果仅供参考。
我们使用Emscripten将C++编译为wasm,这个过程本文不作介绍,只贴出WebAssembly在浏览器中的加载函数:
function loadWebAssembly (path, imports = {}) {
return fetch(path)
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.compile(buffer))
.then(module => {
imports.env = imports.env || {}
imports.env.memoryBase = imports.env.memoryBase || 0
if (!imports.env.memory) {
imports.env.memory = new WebAssembly.Memory({ initial: 256 })
}
imports.env.tableBase = imports.env.tableBase || 0
if (!imports.env.table) {
imports.env.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
}
return new WebAssembly.Instance(module, imports)
})
}
loadWebAssembly('math.wasm')
.then(instance => {
//以下为执行代码,以第一个测试为例
const fibonacci = instance.exports._fibonacci;
let runTimes = 1000000;
console.time('斐波那契数列前'+(runTimes+2)+'项');
fibonacci(runTimes);
console.timeEnd('斐波那契数列前'+(runTimes+2)+'项');
})
斐波那契数列
Javascript代码:
let runTimes = 1000000;
let Fibonacci = new Array(1,1);
console.time('斐波那契数列前'+(runTimes+2)+'项');
for (let i=0; i<runTimes; i++) {
Fibonacci.push(Fibonacci[i]+Fibonacci[i+1]);
};
console.timeEnd('斐波那契数列前'+(runTimes+2)+'项');
C++代码:
int fibonacci (int runTimes) {
style="margin-top: 0px; margin-bottom: 10px; padding: 9.5px; border-radius: 4px; background-color: rgb(239, 239, 239); box-sizing: border-box; font-stretch: normal; font-size: 13px; line-height: 1.42857; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; overflow: auto; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; border: 1px solid rgb(204, 204, 204);">int fibonacci (int runTimes) {
unsigned long int Fibonacci[(runTimes+2)];
Fibonacci[0] = 1;
Fibonacci[1] = 1;
for (int i=0; i<runTimes; i++) {
Fibonacci[i+2] = Fibonacci[i] + Fibonacci[i+1];
};
return Fibonacci[runTimes+1];
}
测试结果:
- WebAssembly 2.5ms
- Javascript 35.1ms
请注意斐波那契数列的第96项已经超过了unsigned long
int的范围,后面那些结果都是溢出的,但是加法计算会继续执行。我们更关心测试的公平,在此不引入大数计算的库。在这项纯加法测试中,WebAssembly的强类型特性占了大便宜,性能领先超过10倍。
自然常数e
Javascript代码:
let precision = 100000000;
let e = 2;
console.time('e');
for (let i=precision; i>0; i--) {
e = ( e / i ) + 1;
};
console.timeEnd('e');
C++代码:
long double e (int precision) {
long double E = 2;
for (int i = precision; i > 0; i--) {
E = ( E / i ) + 1;
};
return E;
}
测试结果:
- WebAssembly 1132.6ms
- Javascript 2400.4ms
每次循环做一次除法运算和一次加法运算。WebAssembly领先一倍的性能。
圆周率π
Javascript代码:
let precision = 100000000;
let pi = 2;
console.time('圆周率');
for (let i=precision; i>0; i--) {
pi = pi * i / (2 * i + 1) + 2;
};
console.timeEnd('圆周率');
C++代码:
long double pi (int precision) {
long double Pi = 2;
for (int i = precision; i > 0; i--) {
Pi = Pi * i / (2 * i + 1) + 2;
};
return Pi;
}
测试结果:
- WebAssembly 1285.6ms
- Javascript 2549.4ms
这里用到了圆周率的公式,计算精度为100000000的圆周率。
WebAssembly依然领先一倍。圆周率公式比计算e要复杂,每次循环多做两次乘法运算和一次加法运算,但两个公式的计算时间相差很小。可见在这两个测试,除法计算占用了大部分的计算时间。
总结
由以上测试结果可见,WebAssembly能成倍提高四则运算的性能。不过由于兼容性等原因,目前并不推荐在生产环境中使用WebAssembly,真是非常遗憾。
本文未经许可禁止转载,如需转载关注微信公众号【工程师加一】并留言。