Search code examples
javascriptperformanceperformance-testingperformance.now

performance.now() returns different values when we change the order of execution


I was just trying to see the performance difference in using || (double pipes, short-circuit operator, which should be faster) instead of |.

But the result gave no insight to the actual performance improvements. If we just reorder the below codes..to execute console log with | first, then it will show more time than the other.

  1. How can I see the performance difference using this or any other way? Also,

  2. Can somebody explain why there is difference when we change the order?

    If performance.now(); behaves like this, I don't see any point in using it.

Please help. Thanks :)

var t3 = performance.now();
console.log(1 | (1),
  1 || (0),
  1 || (-1),
  1 || ("a string"),
  1 || (""),
  1 || (null),
  1 || (undefined),
  1 || (console.log(3)),
  1 || (Infinity))
var t4 = performance.now();
console.log("Call with (double) '||' took " + (t4 - t3) + " milliseconds.");

var t0 = performance.now();
console.log(1 | (1),
  1 | (0),
  1 | (-1),
  1 | ("a string"),
  1 | (""),
  1 | (null),
  1 | (undefined),
  1 | (console.log(3)),
  1 | (Infinity))
var t1 = performance.now();
console.log("Call with single | took " + (t1 - t0) + " milliseconds.");


Solution

    1. How can I see the performance difference using this or any other way?
    2. Can somebody explain why there is difference when we change the order?
    1. Do many tests. Single tests (especially ones that take little time) have an extreme risk of being interfered with by your browser or your OS, which can at any point decide to pause execution, do something else, and come back later.
    2. Warm up the environment. Almost everything that does input/output (such as console.log) need initialisation, which will be done the first time it is used, which can easily disrupt the measured performance by several orders of magnitude.
    3. Strip away everything that does not strictly belong to your test. If you want to compare | vs ||, then don't include console.log in the block that is being timed.

    Some demonstrations:

    Run the following code a couple of times. Observe that:

    1. Often (but not always) the first one takes longer. That's initialisation.
    2. Sometimes one takes multiple times as long as the other. That's interference.

    var t3 = performance.now();
    console.log("herp");
    var t4 = performance.now();
    
    var t0 = performance.now();
    console.log("derp");
    var t1 = performance.now();
    
    console.log("Call with herp took " + (t4 - t3) + " milliseconds.");
    console.log("Call with derp took " + (t1 - t0) + " milliseconds.");

    Also observe that I/O has a horrendous effect on performance: (you'll need to scroll down)

    var discard = function(){}; // just discards everything
    
    var t3 = performance.now();
    for(var i = 0; i < 1000; ++i)
    {
        discard("");
    }
    var t4 = performance.now();
    
    var t0 = performance.now();
    for(var i = 0; i < 1000; ++i)
    {
        console.log("");
    }
    var t1 = performance.now();
    
    console.log("discard() took " + (t4 - t3) + " milliseconds.");
    console.log("console.log() took " + (t1 - t0) + " milliseconds.");

    Taking all that into account, let's write a test case that tests your operations:

    var discard = function(){};
    
    for(var i = 0; i < 1000; ++i)
    {
        discard(1 | (1),
          1 || (0),
          1 || (-1),
          1 || ("a string"),
          1 || (""),
          1 || (null),
          1 || (undefined),
          1 || (console.log(3)),
          1 || (Infinity));
    }
    for(var i = 0; i < 1000; ++i)
    {
        discard(1 | (1),
          1 | (0),
          1 | (-1),
          1 | ("a string"),
          1 | (""),
          1 | (null),
          1 | (undefined),
          1 | (console.log(3)),
          1 | (Infinity));
    }
    
    var t3 = performance.now();
    for(var i = 0; i < 1000; ++i)
    {
        discard(1 | (1),
          1 || (0),
          1 || (-1),
          1 || ("a string"),
          1 || (""),
          1 || (null),
          1 || (undefined),
          1 || (console.log(3)),
          1 || (Infinity));
    }
    var t4 = performance.now();
    
    var t0 = performance.now();
    for(var i = 0; i < 1000; ++i)
    {
        discard(1 | (1),
          1 | (0),
          1 | (-1),
          1 | ("a string"),
          1 | (""),
          1 | (null),
          1 | (undefined),
          1 | (console.log(3)),
          1 | (Infinity));
    }
    var t1 = performance.now();
    
    console.log("Call with (double) '||' took " + (t4 - t3) + " milliseconds.");
    console.log("Call with single | took " + (t1 - t0) + " milliseconds.");

    Note that pretty much all of the time it takes the | case to complete is still coming from console.log, proving only that short-circuiting on || truly works. Now of course this is accurate for | vs || operations that actually involve I/O. If you leave that out however, the results are much close together again:

    (Note that I cranked up the for loop to 10 million, proving just how much time console.log actually takes!)

    var discard = function(){};
    
    for(var i = 0; i < 10000000; ++i)
    {
        discard(1 | (1),
          1 || (0),
          1 || (-1),
          1 || ("a string"),
          1 || (""),
          1 || (null),
          1 || (undefined),
          1 || (Infinity));
    }
    for(var i = 0; i < 10000000; ++i)
    {
        discard(1 | (1),
          1 | (0),
          1 | (-1),
          1 | ("a string"),
          1 | (""),
          1 | (null),
          1 | (undefined),
          1 | (Infinity));
    }
    
    var t3 = performance.now();
    for(var i = 0; i < 10000000; ++i)
    {
        discard(1 | (1),
          1 || (0),
          1 || (-1),
          1 || ("a string"),
          1 || (""),
          1 || (null),
          1 || (undefined),
          1 || (Infinity));
    }
    var t4 = performance.now();
    
    var t0 = performance.now();
    for(var i = 0; i < 10000000; ++i)
    {
        discard(1 | (1),
          1 | (0),
          1 | (-1),
          1 | ("a string"),
          1 | (""),
          1 | (null),
          1 | (undefined),
          1 | (Infinity));
    }
    var t1 = performance.now();
    
    console.log("Call with (double) '||' took " + (t4 - t3) + " milliseconds.");
    console.log("Call with single | took " + (t1 - t0) + " milliseconds.");

    And in spite of all of that, | and || do entirely different things. Not only in terms of execution, but also in terms of the result. Try 1 || 126 vs 1 | 126, or "a" || "b" vs "a" | "b".