为什么我的WebAssembly函数比JavaScript等效函数慢?

为什么我的WebAssembly函数比JavaScript等效函数慢?,webassembly,Webassembly,为这个宽泛的问题道歉!我正在学习WASM,并用C语言创建了一个Mandelbrot算法: int iterateEquation(float x0, float y0, int maxiterations) { float a = 0, b = 0, rx = 0, ry = 0; int iterations = 0; while (iterations < maxiterations && (rx * rx + ry * ry <= 4.0)) {

为这个宽泛的问题道歉!我正在学习WASM,并用C语言创建了一个Mandelbrot算法:

int iterateEquation(float x0, float y0, int maxiterations) {
  float a = 0, b = 0, rx = 0, ry = 0;
  int iterations = 0;
  while (iterations < maxiterations && (rx * rx + ry * ry <= 4.0)) {
    rx = a * a - b * b + x0;
    ry = 2.0 * a * b + y0;
    a = rx;
    b = ry;
    iterations++;
  }
  return iterations;
}

void mandelbrot(int *buf, float width, float height) {
  for(float x = 0.0; x < width; x++) {
    for(float y = 0.0; y < height; y++) {
      // map to mandelbrot coordinates
      float cx = (x - 150.0) / 100.0;
      float cy = (y - 75.0) / 100.0;
      int iterations = iterateEquation(cx, cy, 1000);
      int loc = ((x + y * width) * 4);
      // set the red and alpha components
      *(buf + loc) = iterations > 100 ? 255 : 0;
      *(buf + (loc+3)) = 255;
    }
  }
}
我正在加载JavaScript,编译,然后调用,如下所示:

instance.exports.mandelbrot(0, 300, 150)
正在将输出复制到画布,这使我能够验证它是否正确执行。在我的电脑上,执行上述功能大约需要120毫秒

但是,这里有一个JavaScript等价物:

const iterateEquation = (x0, y0, maxiterations) => {
  let a = 0, b = 0, rx = 0, ry = 0;
  let iterations = 0;
  while (iterations < maxiterations && (rx * rx + ry * ry <= 4)) {
    rx = a * a - b * b + x0;
    ry = 2 * a * b + y0;
    a = rx;
    b = ry;
    iterations++;
  }
  return iterations;
}

const mandelbrot = (data) => {
  for (var x = 0; x < 300; x++) {
    for (var y = 0; y < 150; y++) {
      const cx = (x - 150) / 100;
      const cy = (y - 75) / 100;
      const res = iterateEquation(cx, cy, 1000);
      const idx = (x + y * 300) * 4;
      data[idx] = res > 100 ? 255 : 0;
      data[idx+3] = 255;
    }
  }
}
const iterateEquation=(x0,y0,maxiterations)=>{
设a=0,b=0,rx=0,ry=0;
设迭代次数=0;
而(迭代次数<最大迭代次数&(rx*rx+ry*ry{
对于(变量x=0;x<300;x++){
对于(变量y=0;y<150;y++){
常数cx=(x-150)/100;
常数cy=(y-75)/100;
常数res=迭代方程(cx,cy,1000);
常数idx=(x+y*300)*4;
数据[idx]=res>100?255:0;
数据[idx+3]=255;
}
}
}
只需约62毫秒即可执行

现在我知道WebAssembly是一个非常新的产品,它并没有得到非常好的优化。但我还是忍不住觉得它应该比这个更快

有人能发现我可能错过的东西吗

此外,我的C代码从“0”开始直接写入内存-我想知道这是否安全?堆栈存储在分页线性内存中的什么位置?我是否要冒覆盖它的风险

这里有一把小提琴来说明:

运行时,它会记录两个等效实现(WASM和JavaScript)的时间安排。

General 通常,与优化的JS相比,您可以希望在繁重的数学计算中获得约10%的提升。这包括:

  • 利润
  • 内存输入/输出复制费用
请注意,在chrome中,Uint8Array copy的速度非常慢(在FF中为ok)。当您使用rgba数据时,最好将底层缓冲区重新设置为UInt32阵列ant,并在其上使用
.set()

在wasm中尝试按字读/写像素(rgba)与读/写字节(r、g、b、a)的速度相同。我没有发现差异

当使用
node.js
进行开发时(和我一样)下一个版本将v8升级到v6.0,并为这类数学引入了严重的速度倒退。对于8.2.1,不要使用现代ES6功能,如
const
=>
等。改用ES5。下一个版本使用v8 v6.2可能会解决这些问题

示例注释
  • 使用wasm opt-O3,这可能在发出叮当声-O3后的某个时候有所帮助
  • 使用s2wasm--导入内存而不是硬编码固定内存大小
  • 在wasdk站点的代码中,不要使用全局变量。当这些变量存在时,编译器将在内存开始时为全局变量分配未知块,您可能会错误地重写这些变量
  • 可能,正确的代码应该从适当的位置添加内存副本,这应该包括在基准测试中。您的示例不完整,来自wasdk的IMHO代码应该不能正常工作
  • 使用
    benchmark.js
    ,这更准确

  • 简言之:在继续之前,清理东西是值得的


    你可能会发现挖掘源代码或在实验中使用它很有用。我专门为CPU密集型的小事情创建了它,以通过适当的模块初始化、内存管理、js回退等简化问题。它包含“反锐化掩码”实现作为示例和基准。在那里采用你的代码应该不难。

    我有一个webassembly速度较慢的情况。编译时启用了安全堆选项。删除该选项后,速度大约是本机速度的两倍,因此编译选项也值得寻找。

    您可以提供类似JSFIDLE链接的内容进行尝试吗?您在哪个浏览器中测试?回答了堆栈问题,使用0是安全的在WebAssembly,但是C++编译时可能会不高兴。我只是想让它在WasmFiddle工作,我会尽快处理这个问题。浏览器是Chrome 61。谢谢链接到栈答案。@ JFBASTIAN -我增加了小提琴:我检查了C版本,在初始化浮点数时,我确保它有“.0f”而且性能显著提高。通过这次更改,WebAssembly版本比我笔记本电脑上的JS版本快。但是,在我的桌面上,JS版本仍然比WebAssembly版本快。修改后的fiddle:老实说,很难在文本中解释所有细节。检查多重数学将非常有用这些都是小的,经过战斗考验的,并且有很好的评论。但是规则禁止用单个链接回复:)
    const iterateEquation = (x0, y0, maxiterations) => {
      let a = 0, b = 0, rx = 0, ry = 0;
      let iterations = 0;
      while (iterations < maxiterations && (rx * rx + ry * ry <= 4)) {
        rx = a * a - b * b + x0;
        ry = 2 * a * b + y0;
        a = rx;
        b = ry;
        iterations++;
      }
      return iterations;
    }
    
    const mandelbrot = (data) => {
      for (var x = 0; x < 300; x++) {
        for (var y = 0; y < 150; y++) {
          const cx = (x - 150) / 100;
          const cy = (y - 75) / 100;
          const res = iterateEquation(cx, cy, 1000);
          const idx = (x + y * 300) * 4;
          data[idx] = res > 100 ? 255 : 0;
          data[idx+3] = 255;
        }
      }
    }