-1

Originally I was testing out how adding some codes that caches the result will impact the initial calculation time. I create a simple recursive function that calculates factorials:

function fac(n){
    return n <= 1 ? 1 : fac(n-1) * n;
}

Then I add the part that caches the result for later use:

var f = [];
function fac(n){
     return n <= 1 ? 1 : f[n] ? f[n] : f[n] = fac(n-1) * n;
}

I put them in jsPerf and the result is that the version with cache is unexpectedly faster. I suspect it might be because the array f I used remain the same in the test runner. The function was just grabbing the value from the array and thus it was faster.

To test I created another function that only returns the value from an array:

var test = []; test[10] = 3628800;
function control(n){
    return n <= 1 ? 1 : test[n] ? test[n] : 1;
}

The result says the control is significantly faster than the function with caching added. So that concludes that either the following:

  1. The array f is remain untouched and the difference in ops/sec is caused by the initial calculation.

  2. The array f is being "reset" each time and for some reason it is faster than the normal version.

I don't believe the initial calculation would make the whole test 74% slower than the control, therefore #2 should be true. But what makes it faster than the normal version? From 15,262,318 ops/sec to 114,370,808 ops/sec it's quite significant actually.

Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
  • Why did you expect the cached version to be slower? What kind of invocation did you want to test? – Bergi Jun 16 '14 at 00:53
  • @Bergi - I expect the cached version to be slower since it has to write more data into memory. – Derek 朕會功夫 Jun 16 '14 at 00:55
  • But only for the first invocation! When running about a million times (jsperf tells you long each test loop is), that is completely insignificant. Now compare an array access to 10 function invocations. – Bergi Jun 16 '14 at 00:58
  • Also, could you please link that other test case with the non-sensical `control` function? – Bergi Jun 16 '14 at 00:59
  • @Bergi - What do you mean by "non-sensical"? – Derek 朕會功夫 Jun 16 '14 at 01:41
  • The `control` function that returns hardcoded `3628800` for the input 10 and `1` otherwise. Not sure how you wanted to compare that to a memoizing `fac` function. – Bergi Jun 16 '14 at 01:45

2 Answers2

0

I would suspect the reason that control optimizes so well is because it does no work. A quick look reveals that it only has 2 possible return values: test[10] or 1. Since you never add anything to test in the function, it's probably being optimized down to nearly nothing.

Yeraze
  • 3,269
  • 4
  • 28
  • 42
  • You are right that the browser has optimized the code, but a quick modification to the control function by changing `1` to `control(n)` still yields a faster calculation. – Derek 朕會功夫 Jun 16 '14 at 00:54
  • With or without the assignment? That might lead to very different byte code. – Bergi Jun 16 '14 at 01:00
0

Be careful of performance tests, often what is faster in one host is slower in another.

If you really care about speed, don't use recursion as sequential loops are nearly always significantly faster in all hosts. A factorial function is often used for recursion examples as it's easy to understand and compare with a non–recursive function, not because it's faster.

Using a looping function with stored results should be fastest of all:

var factorial = (function () {
  var facStore = [1,1,2];

  return function (n) {
    var x = n;
    var result = 1;

    if (n == 0 || facStore[n]) return facStore[n];

    while (n) result = result * n--;

    facStore[x] = result;
    return result;
  } 
}());

Which is more to type, but the performance benefits are significant.

RobG
  • 142,382
  • 31
  • 172
  • 209