2

Given:

(function() {
    var items = [1, 2, 3, 4];

    // In Chrome, this takes ~8-10 ms to execute.
    for(var i = 0; i < items.length; i++) {
        x(items);
    }

    // In Chrome, this takes 1-2 ms to execute.
    for(var i = 0; i < items.length; i++) {
        y();
    }

   function x(y) {
        y[0] = -100;
    }

    function y() {
        items[0] = 100;
    }
})();

Why are the calls to x() 8-10 times slower than the calls to y()? Is it because variable resolution does not need to take place in the execution of y()?

digita1-anal0g
  • 501
  • 7
  • 19
  • 1
    How have you profiled the code to determine this? – Alexander O'Mara Aug 13 '16 at 06:54
  • W/ the `Show timestamps` flag set in the settings of the dev console of chrome, I added `console.log(new Date());` calls before and after each for loop and observed the time difference. – digita1-anal0g Aug 13 '16 at 07:04
  • @digita1-anal0g You should use `console.time` and `console.timeEnd` to profile code. – idmean Aug 13 '16 at 07:07
  • curious how much faster it would be if you cached the ".length" – james emanon Aug 13 '16 at 07:13
  • 1
    I see no noticeable difference in either Chrome or node. There could be a difference due to some optimization issues. For better testing, try `var items = Array(1000000);`. Neither of your test cases should take anywhere near 1ms to execute--several microseconds is more like it. –  Aug 13 '16 at 07:16
  • @jamesemanon Well, that doesn't affect the benchmark. In any case, the hoary old-wives-tale about caching `length` has long been debunked. –  Aug 13 '16 at 07:17
  • @torazaburo I do. The second loop is about 11 times faster than the first. (node: `x: 0.659ms y: 0.056ms`) – idmean Aug 13 '16 at 07:21
  • Surely y() is only assigning a value to one element of an existing array, while x() is first storing a copy of items as a new array in memory then assigning a value to one element of it – Darren H Aug 13 '16 at 07:30
  • @gman Without code optimisation. Any reasonable C compiler would compile that difference away. The point is, we don’t know (yet) whether V8 employed code optimisation to this piece of JavaScript, so it’s really hard to say why *exactly* (I think this is the point of the question) there is such a huge difference. Because factor 10-11 is pretty much just for one stack variable, isn’t it? – idmean Aug 13 '16 at 07:31
  • I have no idea about JavaScript optimization but assuming the optimiser does nothing the one with the arguments `x` has to push an argument on to the stack every iteration where was the one calling `y` does not. That difference alone would make `x` slower than `y`. Let add though that doesn't mean you shouldn't use functions. The example is arguably not to useful because `x` and `y` are so small they arguably shouldn't be functions in the first place. More typical functions the difference would **not** likely be a good source of optimization. – gman Aug 13 '16 at 07:31
  • 1
    @DarrenH No. It’s not. JavaScript does pass arrays by reference. – idmean Aug 13 '16 at 07:32
  • @idmean thanks, I stand corrected – Darren H Aug 13 '16 at 07:36
  • @idmean Thanks for the suggestion on `console.time` and `console.timeEnd`. I didn't know about those API's – digita1-anal0g Aug 13 '16 at 13:47
  • @gman The example is intentionally trivial. I'm more interested in why there's a difference in performance and what is the cause. In a much larger application I've found that the performance difference increases. `y()`'s execution time remains within the 1~2 ms range where as `x()` execution time, in my example app, has shot up to 28-30ms. – digita1-anal0g Aug 13 '16 at 13:49
  • I don't know why there's such a big difference but I did give you a valid reason for a difference. Calling `x(times)` translates into `push times on stack; call x` where as `y()` translates into just `call y`. One of those is more work than the other. On top of that a trivial optimizer could remove the call to `y` where as it's a slightly less trivial optimization to remove the call to `x`. The code you posted both could be easily optimized but that's clearly not the code exactly as you timed it as there's no way iterating over 4 times is going to take even 1ms. Post your real code. – gman Aug 13 '16 at 17:20
  • And FYI: Arrays aren't passed by reference. References to arrays are passed by value. There's a subtle difference. Passing a reference = ability to modify the variable outside the function but you're modifying the array, not the the variable `items` referencing to the array. If you could actually pass by reference in JavaScript you could make `items` reference a different array inside `x`. By hey, don't take my word for it: http://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language – gman Aug 13 '16 at 17:52

1 Answers1

0

I'm not seeing a difference in time except for the first iteration or 2 which suggests that there is no big difference except something getting added during start up. V8 doesn't optimize immediately AFAIK so that could explain why it takes a few iterations to balance out. Also cache misses.

function log() {
  var div = document.createElement("div");
  div.textContent = Array.prototype.join.call(arguments, " ");
  document.body.appendChild(div);
};

(function() {
    var items = new Array(10000000);

    for (j = 0; j < 20; ++j) {
      var xStart = performance.now();
      for(var i = 0; i < items.length; i++) {
          x(items);
      }
      var xDuration = performance.now() - xStart;

      var yStart = performance.now();
      for(var i = 0; i < items.length; i++) {
          y();
      }
      var yDuration = performance.now() - yStart;
      log(j, "x:", xDuration.toFixed(3) + "ms",
          "y:", yDuration.toFixed(3) + "ms",
          "diff:", (xDuration - yDuration).toFixed(3) + "ms");
    }
  
    function x(y) {
        y[0] = -100;
    }

    function y() {
        items[0] = 100;
    }
})();
body { font-family: monospace; }
gman
  • 100,619
  • 31
  • 269
  • 393