为什么asm.js会降低性能?

jti*_*one 39 javascript performance bitwise-operators asm.js

为了了解它的表现,我手工编写了一个非常短的asm.js模块,它使用32位整数数学和类型化数组(Int32Array)模拟2D波动方程.我有三个版本,尽可能相似:

  1. 普通的(即易读的,虽然是C风格的)JavaScript
  2. 与Firefox相同,添加了asm.js注释,以便它通过验证器,根据Firefox和其他工具
  3. 与2相同,但没有"使用asm"; 指令在顶部

我在http://jsfiddle.net/jtiscione/xj0x0qk3/上留下了一个演示,它允许您在模块之间切换以查看使用每个模块的效果.这三个都有效,但速度不同.这是热点(使用asm.js注释):

for (i = 0; ~~i < ~~h; i = (1 + i)|0) {
    for (j = 0; ~~j < ~~w; j = (1 + j)|0) {
        if (~~i == 0) {
            index = (1 + index) | 0;
            continue;
        }
        if (~~(i + 1) == ~~h) {
            index = (1 + index) | 0;
            continue;
        }
        if (~~j == 0) {
            index = (1 + index) | 0;
            continue;
        }
        if (~~(j + 1) == ~~w) {
            index = (1 + index) | 0;
            continue;
        }
        uCen = signedHeap  [((u0_offset + index) << 2) >> 2] | 0;
        uNorth = signedHeap[((u0_offset + index - w) << 2) >> 2] | 0;
        uSouth = signedHeap[((u0_offset + index + w) << 2) >> 2] | 0;
        uWest = signedHeap [((u0_offset + index - 1) << 2) >> 2] | 0;
        uEast = signedHeap [((u0_offset + index + 1) << 2) >> 2] | 0;
        uxx = (((uWest + uEast) >> 1) - uCen) | 0;
        uyy = (((uNorth + uSouth) >> 1) - uCen) | 0;
        vel = signedHeap[((vel_offset + index) << 2) >> 2] | 0;
        vel = vel + (uxx >> 1) | 0;
        vel = applyCap(vel) | 0;
        vel = vel + (uyy >> 1) | 0;
        vel = applyCap(vel) | 0;
        force = signedHeap[((force_offset + index) << 2) >> 2] | 0;
        signedHeap[((u1_offset + index) << 2) >> 2] = applyCap(((applyCap((uCen + vel) | 0) | 0) + force) | 0) | 0;
        force = force - (force >> forceDampingBitShift) | 0;
        signedHeap[((force_offset + index) << 2) >> 2] = force;
        vel = vel - (vel >> velocityDampingBitShift) | 0;
        signedHeap[((vel_offset + index) << 2) >> 2] = vel;
        index = (index + 1)|0;
    }
}
Run Code Online (Sandbox Code Playgroud)

"普通JavaScript"版本的结构如上,但没有asm.js所需的按位运算符(例如"x | 0","~~ x","arr [(x << 2)>> 2]",等等.)

这些是我的机器上所有三个模块的结果,使用Firefox(Developer Edition v.41)和Chrome(版本44),每次迭代以毫秒为单位:

  • FIREFOX(版本41):20毫秒,35毫秒,60毫秒.
  • CHROME(版本44):25毫秒,150毫秒,75毫秒.

因此普通的JavaScript在两种浏览器中都获胜 asm.js所需注释的存在使两者的性能降低了3倍.此外,存在"使用asm"; 指令有明显的效果 - 它有点帮助Firefox,并让Chrome瘫痪!

奇怪的是,仅添加按位运算符应该引入三重性能降级,这是通过告诉浏览器使用asm.js无法克服的.另外,为什么告诉浏览器使用asm.js只能在Firefox中略有帮助,并且在Chrome中完全适得其反?

cri*_*n v 9

实际上,没有创建asm.js来手动编写代码,而只是作为其他语言编译的结果.据我所知,没有工具可以验证asm.js代码.您是否尝试使用C lang编写代码并使用Emscripten生成asm.js代码?我强烈怀疑结果将完全不同并针对asm.js进行了优化.

我认为混合类型和无类型的变量只会增加复杂性而没有任何好处.相反,"asm.js"代码更复杂:我试图在jointjs.com/demos/javascript-ast上解析asm.js和普通函数,结果如下:

  • 普通的js函数有137个节点和746个令牌
  • asm.js函数有235个节点和1252个令牌

我会说如果你在每个循环中有更多的指令要执行它很容易.

  • 虽然你的设计不是手工编写你是对的,[这里](http://turtlescript.github.cscott.net/asmjs.html)似乎是一个asm.js验证器,而OP的`AsmWaveModule`通过支票. (3认同)
  • 我刚刚在IE上尝试过它(也没有支持asm.js).普通版本运行正常,但无论是否有指令,注释的存在都会导致IE*崩溃*.这与asm.js无关,但它真的很奇怪. (2认同)