WebAssembly性能实测

2017-09-05

前言

在一次面试中被安利了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];
  }

测试结果:

请注意斐波那契数列的第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领先一倍的性能。


圆周率π

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;
  }

测试结果:

这里用到了圆周率的公式,计算精度为100000000的圆周率。

圆周率公式


 WebAssembly依然领先一倍。圆周率公式比计算e要复杂,每次循环多做两次乘法运算和一次加法运算,但两个公式的计算时间相差很小。可见在这两个测试,除法计算占用了大部分的计算时间。



总结

由以上测试结果可见,WebAssembly能成倍提高四则运算的性能。不过由于兼容性等原因,目前并不推荐在生产环境中使用WebAssembly,真是非常遗憾。



本文未经许可禁止转载,如需转载关注微信公众号【工程师加一】并留言。