简体   繁体   English

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

[英]Why does asm.js deteriorate performance?

Just to see how it performs, I wrote a very short asm.js module by hand, which simulates the 2D wave equation using 32-bit integer math and typed arrays (Int32Array). 为了了解它的表现,我手工编写了一个非常短的asm.js模块,它使用32位整数数学和类型化数组(Int32Array)模拟2D波动方程。 I have three versions of it, all as similar as possible: 我有三个版本,尽可能相似:

  1. Ordinary (ie legible, albeit C-style) JavaScript 普通的(即易读的,虽然是C风格的)JavaScript
  2. Same as 1, with asm.js annotations added so that it passes the validator, according to Firefox and other tools 与Firefox相同,添加了asm.js注释,以便它通过验证器,根据Firefox和其他工具
  3. Same as 2, except with no "use asm"; 与2相同,但没有“使用asm”; directive at the top 指令在顶部

I left a demo at http://jsfiddle.net/jtiscione/xj0x0qk3/ which lets you switch between modules to see the effects of using each one. 我在http://jsfiddle.net/jtiscione/xj0x0qk3/上留下了一个演示,它允许您在模块之间切换以查看使用每个模块的效果。 All three work, but at different speeds. 这三个都有效,但速度不同。 This is the hotspot (with asm.js annotations): 这是热点(使用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;
    }
}

The "ordinary JavaScript" version is structured as above, but without the bitwise operators that asm.js requires (eg "x|0", "~~x", "arr[(x<<2)>>2]", etc.) “普通JavaScript”版本的结构如上,但没有asm.js所需的按位运算符(例如“x | 0”,“~~ x”,“arr [(x << 2)>> 2]”,等等。)

These are the results for all three modules on my machine, using Firefox (Developer Edition v. 41) and Chrome (version 44), in milliseconds per iteration: 这些是我的机器上所有三个模块的结果,使用Firefox(Developer Edition v.41)和Chrome(版本44),每次迭代以毫秒为单位:

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

So ordinary JavaScript wins in both browsers. 因此普通的JavaScript在两种浏览器中都获胜 The presence of asm.js-required annotations deteriorates performance by a factor of 3 in both. asm.js所需注释的存在使两者的性能降低了3倍。 Furthermore, the presence of the "use asm"; 此外,存在“使用asm”; directive has an obvious effect- it helps Firefox a bit, and brings Chrome to its knees! 指令有明显的效果 - 它有点帮助Firefox,并让Chrome瘫痪!

It seems strange that merely adding bitwise operators should introduce a threefold performance degradation that can't be overcome by telling the browser to use asm.js. 奇怪的是,仅添加按位运算符应该引入三重性能降级,这是通过告诉浏览器使用asm.js无法克服的。 Also, why does telling the browser to use asm.js only help marginally in Firefox, and completely backfire in Chrome? 另外,为什么告诉浏览器使用asm.js只能在Firefox中略有帮助,并且在Chrome中完全适得其反?

Actually asm.js has not been created to write code by hand but only as result of a compilation from other languages. 实际上,没有创建asm.js来手动编写代码,而只是作为其他语言编译的结果。 As far as I know there are no tools that validate the asm.js code. 据我所知,没有工具可以验证asm.js代码。 Have you tried to write the code in C lang and use Emscripten to generate the asm.js code? 您是否尝试使用C lang编写代码并使用Emscripten生成asm.js代码? I strongly suspect that the result would be quite different and optimized for asm.js. 我强烈怀疑结果将完全不同并针对asm.js进行了优化。

I think that mixing typed and untyped vars you only add complexity without any benefits. 我认为混合类型和无类型的变量只会增加复杂性而没有任何好处。 On the contrary the "asm.js" code is more complex: I tried to parse the asm.js and the plain functions on jointjs.com/demos/javascript-ast and the results are: 相反,“asm.js”代码更复杂:我试图在jointjs.com/demos/javascript-ast上解析asm.js和普通函数,结果如下:

  • the plain js function has 137 nodes and 746 tokens 普通的js函数有137个节点和746个令牌
  • the asm.js function has 235 nodes and 1252 tokens asm.js函数有235个节点和1252个令牌

I would say that if you have more instructions to execute in each loop it easily will be slower. 我会说如果你在每个循环中有更多的指令要执行它很容易。

There is some fix cost to switching asm.js contexts. 切换asm.js上下文有一些修复成本。 Ideally you do it once and run all of your code within your app as asm.js. 理想情况下,您只需执行一次并在应用程序中运行所有代码as as.js. Then you can control memory management using typed arrays and avoid lots of garbage collections. 然后,您可以使用类型化数组控制内存管理,并避免大量垃圾收集。 I'd suggest to rewrite the profiler and measure asm.js within asm.js - without context switching. 我建议重写探查器并测量asm.js中的asm.js - 没有上下文切换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM