Comparison of memoization implementations

JavaScript performance comparison

Revision 18 of this test case created by JasonG

Preparation code

<script>
  //memo6 - by @madrobby/thomas fuchs
 
  Function.prototype.cached = function(){
    var self = this, cache = {};
    return function(arg){
      if(arg in cache) {
        //console.log('Cache hit for '+arg);
        return cache[arg];
      } else {
        //console.log('Cache miss for '+arg);
        return cache[arg] = self(arg);
      }
    }
  }

Function.prototype.cached7 = function(){
    "use strict";
    var self = this, cache7 = {};
    return function(arg){
        if(cache7[arg] !== undefined) {
            //console.log('Cache hit for '+arg);
            return cache7[arg];
        } else {
            //console.log('Cache miss for '+arg);
            return cache7[arg] = self(arg);
        }
    };
}
 
 
  //memo1
  //underscore.js implem
 
  function memoize1(func, hasher) {
      var memo = {};
      hasher || (hasher = function(value){
        return value; //^this could be done better..surely..
      });
      return function() {
        var key = hasher.apply(this, arguments);
        return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
      };
    };
 
 
  //memo2 - another variation with changes by @addyosmani
  //based on initial work by thejit
  //note: I'm aware of the perils of hanging the implem off
  //fn, just want to find out if this version performs much better
    function memoize2(fn) {
      return function () {
          var args = Array.prototype.slice.call(arguments),
              hash = "",
              i  = args.length;
          while(i--){
            hash += (Object.prototype.toString.call({}) == Object.prototype.toString.call(args[i])) ? JSON.stringify(args[i]) : args[i];
            fn.memoize = fn.memoize || {};
        }
          return (hash in fn.memoize) ? fn.memoize[hash] : fn.memoize[hash] = fn.apply(this, args);
      };
  }
 
  //memo3 - unscriptables implem.
  function memoize3(func, context) {
      function memoizeArg (argPos) {
          var cache = {};
          return function () {
              if (argPos == 0) {
                  if (!(arguments[argPos] in cache)) {
                      cache[arguments[argPos]] = func.apply(context, arguments);
                  }
                  return cache[arguments[argPos]];
              }
              else {
                  if (!(arguments[argPos] in cache)) {
                      cache[arguments[argPos]] = memoizeArg(argPos - 1);
                  }
                  return cache[arguments[argPos]].apply(this, arguments);
              }
          }
      }
      // JScript doesn't grok the arity property, but uses length instead
      var arity = func.arity || func.length;
      return memoizeArg(arity - 1);
  }
 
  //memo4 - by stevenlevithan
  function memoize4(functor, expiration) {
        var memo = {};
        return function () {
                var key = Array.prototype.join.call(arguments, "§");
                if (key in memo)
                        return memo[key];
                if (expiration)
                        setTimeout(function () {delete memo[key];}, expiration);
                return memo[key] = functor.apply(this, arguments);
        };
  }
 
 
  //memo5 - @gbradley
 
  function memoize5(fn){
  var lookup={};
  return function(){
        var args=[].slice.call(arguments);
        var key=JSON.stringify(args);
        var result=lookup[key];
        if (!result){
                result=fn.apply(this,args);
                lookup[key]=result;
                }
        return result;
        };
  }

function memoize7( fn ) {
return function () {
        var args = Array.prototype.slice.call(arguments),
            hash = "",
            i  = args.length,
            hasHash = false;
        while(i){
            hash += (Object.prototype.toString.call({}) == Object.prototype.toString.call(args[--i])) ? JSON.stringify(args[i]) : args[i];
            fn.memoize = fn.memoize || {};
        }
        hasHash = fn.memoize[hash] !== undefined;
        return (hasHash) ? fn.memoize[hash] : fn.memoize[hash] = fn.apply(this, args);
    };
}
 
  function fib(x) {
      if(x < 2) return 1; else return fib(x-1) + fib(x-2);
  }

  // Redefine to prevent any unknown special inlines...
  function fib7(x) {
      if(x<2) return 1; else return fib7(x-1) + fib7(x-2);
  }
 
var fibTest1 = memoize1(fib);
var fibTest2 = memoize2(fib);
var fibTest3 = memoize3(fib);
var fibTest4 = memoize4(fib,1000);
var fibTest5 = memoize5(fib);
var fibTest6 = fib.cached();
var fibTest7 = memoize7(fib);
var fibTest7c = fib7.cached7();

</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Memo 1 - underscore.js
fibTest1(20);
fibTest1(20);
pending…
Memo 2 - addy + thejit
fibTest2(20);
fibTest2(20);
pending…
Memo 3 - unscriptable
fibTest3(20);
fibTest3(20);
pending…
Memo 4 - stevenlevithan
fibTest4(20);
fibTest4(20);
pending…
Memo 5 - gbradley
fibTest5(20);
fibTest5(20);
pending…
Memo 6 - madrobby
fibTest6(20);
fibTest6(20);
pending…
Memo 7
fibTest7(20);
fibTest7(20);
pending…
Memo 7 Cached
fibTest7c(20);
fibTest7c(20);
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

1 comment

JasonG (revision owner) commented :

Also see: http://jsperf.com/hasownproperty-vs-in-vs-other/5 http://jsperf.com/whiletestingmore http://jsperf.com/while-testing

Add a comment