31

I am trying to understand how exactly ASM works and when it kicks in.

I took a small function from the asm.js website. I wrap it using the module pattern: once for asm, once with the same syntax but without the "use asm" annotation, and once like vanilla-javascript.

 var add_asm = (function MyAOTMod(stdlib, foreign, heap) {
  "use asm";
  var sqrt = stdlib.Math.sqrt;

  function square(x) {
    x = +x;
    return +(x * x);
  }
  return function(x, y) {
    x = +x; // x has type double
    y = +y; // y has type double
    return +sqrt(square(x) + square(y));
  };
}(window));

var add_reg_asmstyle = (function MyAsmLikeRegularMod() {

  function square(x) {
    x = +x;
    return +(x * x);
  }
  return function(x, y) {
    x = +x; // x has type double
    y = +y; // y has type double
    return +Math.sqrt(square(x) + square(y));
  };
}());


var add_reg = (function MyStrictProfile() {
  "use strict";
  return function(x, y) {
    return Math.sqrt(x * x + y * y);
  };
}())

I created a small jsperf: the jsperf code is slightly different from the above, incorporating tips from the discussion thread below http://jsperf.com/asm-simple/7

The performance shows that firefox 22 is slowest with the asm-syntax (with or without the "use asm" annotation), and chrome is fastest in asm-mode.

So my question is: how is this possible? I would expect Firefox to be fastest in asm mode. I would not expect to see a difference for Chrome. Do I use a wrong asm syntax? What am I missing?

Any advice or clarification is greatly appreciated. Thanks,

c69
  • 19,951
  • 7
  • 52
  • 82
Thomas
  • 391
  • 3
  • 6
  • I don't know anything about whatever ASM is, but why do you think Firefox would be fastest? You know they use different JavaScript engines, right? All browsers differ in performance – Ian Jul 30 '13 at 15:43
  • Call the function once in the setup (to trigger the compilation) and then see the result – SheetJS Jul 30 '13 at 15:47
  • 3
    @Ian: OP is comparing the ASM and non-ASM code in each browser to see the difference it makes. Chrome doesn't support ASM optimizations AFAIK, so the annotation shouldn't make a difference, while FireFox does support it. OP isn't comparing FF to Chrome. –  Jul 30 '13 at 15:49
  • 2
    @Thomas: I'd be curious to know why you have small variations in each function. The first one caches `Math.sqrt`, the second one doesn't, and the third doesn't use a function for rounding, and doesn't use the unary `+` operator. I don't know that any of that would make a difference, but I'd think you'd want to keep it consistent. –  Jul 30 '13 at 15:53
  • @Nirk: I added a call to the asm function. (http://jsperf.com/asm-simple/2). It seems to behave the same. – Thomas Jul 30 '13 at 15:55
  • 6
    You are not supposed to write ASM.js code by hand, because you will almost certainly be unable to (note: using "use asm" in non-ASM.js-compliant code, will fallback silently to regular JS execution). It is supposed to be a target for code generated by a compiler like [emscripten](http://emscripten.org). – CAFxX Jul 30 '13 at 15:57
  • @CrazyTrain you're right about the syntax. I modified it: http://jsperf.com/asm-simple/2. I just didnt want to do this at first because in regular js i'd probably would not write the function the asm way (with the extra func calls, like on the asn.js website). ff indeed performs the same in all 3 cases when rewritten. – Thomas Jul 30 '13 at 16:03
  • @CAFxX I understand. But I am just trying to understand here why FF would not show a performance benefit; trying to get my feet wet with the basics really. I am also not sure where the non-compliance would be in the example code: any ideas? – Thomas Jul 30 '13 at 16:09
  • 8
    @CAFxX if it is just javascript, you should be able to write it by hand. Just like how you can (and people do) write x86 assembly directly. – SheetJS Jul 30 '13 at 16:12
  • @CrazyTrain I see I see, I obviously misread. I'm already losing it today... – Ian Jul 30 '13 at 16:33
  • Firefox' builtin console says "TypeError: asm.js type error: Disabled by debugger", when using jsfiddle (without benchmark.js' overhead) it says "TypeError: asm.js type error: non-expression-statement call must be coerced" (the DiagModule example by asmjs.org in Firefox v24a): http://jsfiddle.net/LMpHu/ – metadings Jul 30 '13 at 17:14
  • someone also made a benchmark for DiagModule, it seems it simply doesn't work currently: http://jsperf.com/asm-js – metadings Jul 30 '13 at 17:45
  • @metadings thanks for the links. Not sure what to make of this.Maybe the example of a distance of a 2d vector is too trivial? Now for me the focus of the question seems to shift a little bit: First, why is it that Chrome is faster in the cases where your program is "asm" style (with the "+" to flag numbers as a double) - with or without the "use asm" declaration? Is this an optimization in V8? – Thomas Jul 31 '13 at 08:49
  • Second, it seemed to me that asm.js would have been a good way to make small incremental improvements to existing JS code-bases. For example, you could refactor your bottleneck math functions in your code-base to asm style and get a free "win", at least on firefox (yes, even manually, if it's just prepending some plusses ;)). This does not seem to be the case - is that a correct conclusion? I imagined asm sort of the same way you can use "use strict" to tidy up existing code bases over time, incrementally improving the quality. Instead, with asm, you use it for performance gains – Thomas Jul 31 '13 at 08:50
  • @Nirk I didn't say you should not. I said "you are not supposed to write ASM.js code by hand, because you will almost certainly be unable to". As the code posted by the OP clearly proved. – CAFxX Jul 31 '13 at 09:18
  • apart from the question if its ok to write by hand, does anybody have any ideas know what the mistake in the asm module is? Some missing type coercion apparently, but its hard for me to understand why the hello-world from asm's site does not seem to work as is. Maybe i've made a dumb mistake somewhere, but I cant spot it. – Thomas Jul 31 '13 at 09:42
  • nah. your bench test wasn't asm. you made the most calculations outside of an asm-style js. the thing is that a module is defined by a `function MyAsm(stdlib) { "use asm"; function Calc(i) { i = i|0; } return { Calc: Calc }; }` i can't prove that for now, but important is, that you don't define a function by `var fn = function` but by name `function fn` and that you return an 'export object', that defines references to 'your asm functions'. then you invoke the compiler by `var module = MyAsm({ Math: Math });` and use asm-compiled functions by `module.Calc(i)`. – metadings Jul 31 '13 at 17:43
  • thanks for the hints. tried it http://jsperf.com/asm-simple/7 – Thomas Jul 31 '13 at 20:04
  • only using function declaration doesnt seem to help. note though that the asm spec says the return of a module may either be a func of an object: "An asm.js module's export declaration is a ReturnStatement returning either a single asm.js function or an object literal exporting multiple asm.js functions. " (section 6.2). Is there a reason why an asm module should not be self-invoking, using var mymod = (function(){"use asm.js";...stuff..return something;}()); kindof style? – Thomas Jul 31 '13 at 20:10
  • i don't get why you insist on that bogus math.random numbers thing and modulo calculations - these are even outside of the asm module. however, also in the current v24 aurora i can't get it working because of bogus coersion errors and the debugger that fails on showing the correct line of code. the results on my computer are always faster in non-asm mode... don't know, sorry. – metadings Aug 02 '13 at 17:09
  • now I got the nice "Error: successfully compiled asm.js code" but it is 10 times slower as the non-asm way: http://jsperf.com/asm-js/5 – metadings Aug 02 '13 at 17:15
  • @metadings yeah, i was hesitant with wrapping it in the loops, but I wanted to avoid weird JIT optimizations, if any. Its a habit I picked up when writing benchmarks, maybe I should let it go. The added loop just introduces a constant factor anyways. Thanks for your new perf tests: results remain counter-intuitive. Maybe its time to take this to FFs dev group... – Thomas Aug 08 '13 at 19:23
  • 1
    Implementation details of browsers can have non-intuitive effects. – Peter Trypsteen Sep 03 '13 at 17:14

1 Answers1

8

When you run code in Firefox, you can often see huge drop in speed for asm.js calls, which is most probably caused either by repeated compilation (which is visible in console) or by cost of js-to-asm calls. This hypothesis is further strenghtened by Luke Wagner, implementor of asm.js:

one performance fault that we already know trips up people trying to benchmark asm.js is that calling from non-asm.js into asm.js and vice versa is much slower than normal calls due to general-purpose enter/exit routines. We plan to fix this in the next few months but, in the meantime, for benchmarking purposes, try to keep the whole computation happening inside a single asm.js module, not calling in and out.

To see it for yourself - look at the fiddle: http://jsperf.com/asm-simple/10

  • Firefox 26: 22,600K ops/sec in asm-asm case vs 300(!) in asm-js case.
  • Chrome 28: 18K vs 13K
  • IE11: ~7.5K for all tests, no big difference observed, except for dead code ellimination, where it shines ;)
c69
  • 19,951
  • 7
  • 52
  • 82
  • // no idea if warning `TypeError: asm.js type error: arguments to a comparison must both be signed, unsigned or doubles; int and int are given` is genuine or a bug. Speed boost observed makes me belive its just a junk message. – c69 Sep 04 '13 at 00:00
  • "Dead code elimination" is rarely possible (if ever) in Javascript because of the evil `eval` function and its equally wicked counter-part: `Proxy`. – Jack G Nov 14 '18 at 21:21