another-memoization-comparison (v3)

Revision 3 of this benchmark created by Scott Sauyet on


Setup

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 ("" in cache) ? cache[""] : (cache[""] = fn());
            } else {
                arg = arguments[0];
                args = slice.call(arguments, 1);
                return ((arg in cache) ? cache[arg] : (cache[arg] = memoize( function () {
                    return fn.apply(this, [].concat(arg, slice.call(arguments)));
                }))).apply(this, args)
            }
        }
    };
    
    fib9 = memoize9(getFibFn());

Test runner

Ready to run.

Testing in
TestOps/sec
Memo 1 - underscore.js
fib1(20);
ready
Memo 2 - addy, mathias, jamie etc.
fib2(20);
ready
Memo 3 - unscriptable
fib3(20);
ready
Memo 4 - stevenlevithan
fib4(20)
ready
Memo 5 - gbradley
fib5(20);;
ready
Memo 6 - madrobby
fib6(20);
ready
Memo 7 - medikoo (generic - any type of arguments)
fib7(20);
ready
Memo 8 - no slice
fib8(20);
ready
Memo 9 - Scott Sauyet
fib9(20);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.

  • Revision 1: published by Scott Sauyet on
  • Revision 3: published by Scott Sauyet on
  • Revision 4: published by Scott Sauyet on
  • Revision 5: published on