another-memoization-comparison

JavaScript performance comparison

Test case created by Scott Sauyet and last updated

Preparation code


      
      <script>
Benchmark.prototype.setup = function() {
  var getFibFn = function() {
      return function fib(x) {
          if(x < 2) return 1; else return fib(x - 1) + fib(x - 2);
      };
  };
  
  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));
      };
  };
  fib1 = memoize1(getFibFn());
  
  //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);
      };
  };
  fib2 = memoize2(getFibFn());
  
  //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);
  }
  fib3 = memoize3(getFibFn());
  
  
  //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);
      };
  };
  fib4 = memoize4(getFibFn());
  
  //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;
      };
  };
  fib5 = memoize5(getFibFn());
  
  
  //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);
          }
      };
  };
  fib6 = getFibFn().cached();
  
  // memo7 - @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);
          };
      };
  }());
  fib7 = memoize7(getFibFn());
  
  
   //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));
  fib8 = this.memoize8(getFibFn());
  
  
  // Scott Sauyet - http://articles.local/articles/FunctionalMemoizing/
  var memoize9 = function memoize(fn) {
      var cache = {}, slice = [].slice;
      return function() {
          var args, arg;
          if (arguments.length === 0) {
              return (args in cache) ? cache[args] : (cache[args] = fn());
          } else {
              arg = arguments[0];
              args = slice.call(arguments, 1);
              return (cache[arg] || (cache[arg] = memoize( function () {
                  return fn.apply(this, [].concat(arg, slice.call(arguments)));
              }))).apply(this, args);
          }
      }
  };
  fib9 = memoize9(getFibFn());

};
</script>

Test runner

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

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
Memo 9 - Scott Sauyet
fib9(20);
pending…
Memo 1 - underscore.js
fib1(20);
pending…
Memo 2 - addy, mathias, jamie etc.
fib2(20);
pending…
Memo 3 - unscriptable
fib3(20);
pending…
Memo 4 - stevenlevithan
fib4(20)
pending…
Memo 5 - gbradley
fib5(20);;
pending…
Memo 6 - madrobby
fib6(20);
pending…
Memo 8 - no slice
fib8(20);
pending…
Memo 7 - medikoo (generic - any type of arguments)
fib7(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.

0 Comments