Comparison of memoization implementations

JavaScript performance comparison

Revision 16 of this test case created

Preparation code

<script>
//memo10 - by @yoannSitbon
Function.prototype.memoize10 = function () {
    var self  = this,
        cache = (self.memoize = self.memoize || {}),
        strfy = JSON.stringify,
        slice = Array.prototype.slice;

    return function () {
        var hash = strfy(slice(arguments));
        return (hash in cache) ?
            cache[hash] :
            cache[hash] = self.apply(this, arguments);
    };
};

//memo9 - by @yoannSitbon
Function.prototype.memoize9 = function () {
    'use strict';
    var self = this, cache = (self.memoize = self.memoize || {}),
        stringifyJson = JSON.stringify;

    return function () {
        var hash = stringifyJson(arguments);
        return (hash in cache) ?
            cache[hash] : cache[hash] = self.apply(this, arguments);
    };
};

//memo8 - by @AutoSponge
(function (GLOBAL, stringifyJson) {    
    "use strict";    
    GLOBAL.memoize8 = GLOBAL.memoize8 || stringifyJson && function (func) {
        var cache = (func.memoize = func.memoize || {});
        return function () {            
            var hash = stringifyJson(arguments);            
            return (hash in cache) ? cache[hash] : cache[hash] = func.apply(this, arguments);        
        };    
    } || function (func) {        
        return func;    
    };
}(this, typeof JSON !== "undefined" && JSON.stringify));

  //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);
      }
    }
  }
 
 
  //memo1
  //underscore.js implem
 
  //_.memoize
  function memoize1(func) {
      "use strict";
      var memo = {};
      var slice = Array.prototype.slice;
      return function() {
        var key = "" + slice.call(arguments);
        return (key in memo) ? memo[key] : (memo[key] = func.apply(this, arguments));
      };
    };
 
 
  //memoize.js - by @addyosmani
  // with tweaks from @philogb, @mathias, @DmitryBaranovsk, @JamieMason
  function memoize2(func) {
       "use strict";
       var cache = (func.memoize = func.memoize || {}),
           stringifyJson = JSON.stringify,
           sliceArray = Array.prototype.slice;
        return function () {
            var hash = stringifyJson(sliceArray.call(arguments));
            return (hash in cache) ? cache[hash] : cache[hash] = func.apply(this, arguments);
        };
    };
 
  //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;
        };
  }
 
  // memo5 - @medikoo
  // https://github.com/medikoo/es5-ext/blob/master/lib/Function/memoize.js
 
  var memoize7 = (function () {
        var isArray = Array.isArray
          , slice   = Array.prototype.slice
 
          , resolve;
 
        resolve = function (args) {
                return this.map(function (r, i) {
                        return r ? r(args[i]) : args[i];
                }).concat(slice.call(args, this.length));
        };
 
        return function (fn, length, resolvers) {
                var cache, resolver;
                cache = [];
                if (isArray(length)) {
                        resolvers = length;
                        length = fn.length;
                } else if (length == null) {
                        length = fn.length;
                }
 
                resolver = resolvers ? resolve.bind(resolvers) : null;
 
                return function () {
                        var limit, i, index, args, current, found;
 
                        args = resolver ? resolver(arguments) : arguments;
                        i = 0;
                        index = limit = (length === true) ? args.length: length;
                        current = cache;
 
                        if (limit === 0) {
                                found = current.hasOwnProperty(0);
                        } else {
                                while (i !== limit) {
                                        if (!current[index]) {
                                                current = (current[index] = [[args[i]], []]);
                                                index = 0;
                                        } else if (
                                                (index = (current = current[index])[0].indexOf(args[i])) === -1) {
                                                index = current[0].push(args[i]) - 1;
                                                found = false;
                                        } else {
                                                found = current[1].hasOwnProperty(index);
                                        }
                                        current = current[1];
                                        ++i;
                                }
                        }
                        if (found) {
                                return current[index];
                        }
                        return current[index] = fn.apply(this, args);
                };
        };
  }());
 
  var fib, fiborg;
  fiborg = fib = function (x) {
      if(x < 2) return 1; else return fib(x - 1) + fib(x - 2);
  }
 
</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 2 - addy, mathias, jamie etc.
fib = memoize2(fiborg);
fib(20);
pending…
Memo 1 - underscore.js
fib = memoize1(fiborg);
fib(20);
 
pending…
Memo 3 - unscriptable
fib = memoize3(fiborg);
fib(20);
 
pending…
Memo 4 - stevenlevithan
fib = memoize4(fiborg, 1000);
fib(20);
 
pending…
Memo 5 - gbradley
fib = memoize5(fiborg);
fib(20);
 
pending…
Memo 6 - madrobby
fib = fiborg.cached();
fib(20);
 
pending…
Memo 7 - medikoo (generic - any type of arguments)
fib = memoize7(fiborg);
fib(20);
 
pending…
Memo 8 - no slice
fib = memoize8(fiborg);
fib(20);
pending…
Memo9 - memoize prototype without slice
fib = fiborg.memoize9();
fib(20);
pending…
Memo10 - memoize prototype with slice
fib = fiborg.memoize10();
fib(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:

0 comments

Add a comment