L2O class or functions

JavaScript performance comparison

Revision 3 of this test case created by Marc Brooks (@IDisposable)

Preparation code

 
<script>
Benchmark.prototype.setup = function() {
    // Function based
    function noop () {}
   
    var Enumerator1 = function (moveNext, getCurrent, dispose) {
        this.moveNext = moveNext;
        this.getCurrent = getCurrent;
        this.dispose = dispose;
    };
   
    var enumeratorCreate = Enumerator1.create = function (moveNext, getCurrent, dispose) {
        var done = false;
        dispose || (dispose = noop);
        return new Enumerator1(function () {
            if (done) {
                return false;
            }
            var result = moveNext();
            if (!result) {
                done = true;
                dispose();
            }
            return result;
        }, function () { return getCurrent(); }, function () {
            if (!done) {
                dispose();
                done = true;
            }
        });
    };
   
    function Enumerable1(getEnumerator) {
        this.getEnumerator = getEnumerator;
    }
   
    Enumerable1.prototype.map = function (selector, thisArg) {
        var parent = this;
        return new Enumerable1(function () {
            var current, index = 0, e;
            return enumeratorCreate(
                function () {
                    e || (e = parent.getEnumerator());
                    if (!e.moveNext()) {
                        return false;
                    }
                    current = selector.call(thisArg, e.getCurrent(), index++, parent);
                    return true;
                },
                function () { return current; },
                function () { e.dispose(); }
            );
        });
    };
   
    Enumerable1.prototype.filter = function (selector, thisArg) {
        var parent = this;
        return new Enumerable1(function () {
            var current, index = 0, e;
            return enumeratorCreate(
                function () {
                    e || (e = parent.getEnumerator());
                    while (true) {
                        if (!e.moveNext()) {
                            return false;
                        }
                        var c = e.getCurrent();
                        if (selector.call(thisArg, c, index++, parent)) {
                            current = c;
                            return true;
                        }
                    }
                },
                function () { return current; },
                function () { e.dispose(); }
            );
        });
    };
   
    Enumerable1.prototype.forEach = function (action, thisArg) {
        var e = this.getEnumerator(), i = 0;
        try {
            while (e.moveNext()) {
                action.call(thisArg, e.getCurrent(), i++, this);
            }
        } catch(ex) {
            throw ex;
        } finally {
            e.dispose();
        }
    };  
   
    Enumerable1.range = function (start, count) {
        return new Enumerable1(function () {
            var current = start - 1, end = start + count - 1;
            return enumeratorCreate(
                function () {
                    if (current < end) {
                        current++;
                        return true;
                    } else {
                        return false;
                    }
                },
                function () { return current; }
            );
        });
    };
   
    // Class based
    var inherits = function (child, parent) {
        function __() { this.constructor = child; }
        __.prototype = parent.prototype;
        child.prototype = new __();
    };
   
   
    var Enumerator2 = (function () {
        function Enumerator2() {
            this.current = null;
            this.done = false;
        }
   
        Enumerator2.prototype.current = null;
        Enumerator2.prototype.done = false;
        Enumerator2.prototype.moveNext = function () {
            if (this.done) {
                return false;
            }
            var result = this._moveNext();
            if (!result) {
                this.done = true;
                this.dispose();
            }
            return result;
        };
   
        Enumerator2.prototype.getCurrent = function () {
            return this.current;
        };
   
        Enumerator2.prototype.dispose = function () {
            if (!this.done) {
                this._dispose();
                this.done = true;
            }
        };
   
        Enumerator2.prototype._dispose = noop;
        Enumerator2.prototype._moveNext = noop;
   
        return Enumerator2;
    }());
   
    var Enumerable2 = (function () {
        function Enumerable2() { }
   
        Enumerable2.prototype.filter = function (predicate, thisArg) {
            return new FilterEnumerable2(this, predicate, thisArg);
        };
   
        Enumerable2.prototype.forEach = function (action, thisArg) {
            var i = 0, e = this.getEnumerator();
            try {
                while (e.moveNext()) {
                    action.call(thisArg, e.getCurrent(), i++, this);
                }
            } catch (ex) {
                throw ex;
            } finally {
                e.dispose();
            }
        };
   
        Enumerable2.prototype.map = function (selector, thisArg) {
            return new MapEnumerable2(this, selector, thisArg);
        };
   
        Enumerable2.range = function (start, count) {
            return new RangeEnumerable2(start, count);
        };
   
        return Enumerable2;
    }());
   
    /* Map */
   
    var MapEnumerable2 = (function (super_) {
        inherits(MapEnumerable2, super_);
   
        function MapEnumerable2(parent, selector, thisArg) {
            this.parent = parent;
            this.selector = selector;
            this.thisArg = thisArg;
        }
   
        MapEnumerable2.prototype.getEnumerator = function () {
            return new MapEnumerator2(this.parent, this.selector, this.thisArg);
        };
   
        return MapEnumerable2;
   
    }(Enumerable2));
   
    var MapEnumerator2 = (function (super_) {
        inherits(MapEnumerator2, super_);
   
        function MapEnumerator2(parent, selector, thisArg) {
            this.e = parent.getEnumerator();
            this.selector = selector;
            this.thisArg = thisArg;
            this.index = 0;
        }
   
        MapEnumerator2.prototype._moveNext = function () {
            if (!this.e.moveNext()) {
                return false;
            }
            this.current = this.selector.call(this.thisArg, this.e.getCurrent(), this.index++, this.parent);
            return true;
        };
   
        MapEnumerator2.prototype._dispose = function () {
            this.e.dispose();
        };
   
        return MapEnumerator2;
    }(Enumerator2));
   
    /* Filter */
   
    var FilterEnumerable2 = (function (super_) {
        inherits(FilterEnumerable2, super_);
   
        function FilterEnumerable2(parent, predicate, thisArg) {
            this.parent = parent;
            this.predicate = predicate;
            this.thisArg = thisArg;
        }
   
        FilterEnumerable2.prototype.getEnumerator = function () {
            return new FilterEnumerator2(this.parent, this.predicate, this.thisArg);
        };
   
        return FilterEnumerable2;
   
    }(Enumerable2));
   
    var FilterEnumerator2 = (function (super_) {
        inherits(FilterEnumerator2, super_);
   
        function FilterEnumerator2(parent, predicate, thisArg) {
            this.e = parent.getEnumerator();
            this.predicate = predicate;
            this.thisArg = thisArg;
            this.index = 0;
        }
   
        FilterEnumerator2.prototype._moveNext = function () {
            while (true) {
                 if (!this.e.moveNext()) {
                    return false;
                }
                var c = this.e.getCurrent();
                if (this.predicate.call(this.thisArg, c, this.index++, this.parent)) {
                    this.current = c;
                    return true;
                }          
            }
        };
   
        FilterEnumerator2.prototype._dispose = function () {
            this.e.dispose();
        };
   
        return FilterEnumerator2;
    }(Enumerator2));
   
    /* Range */
   
    var RangeEnumerable2 = (function (super_) {
        inherits(RangeEnumerable2, super_);
   
        function RangeEnumerable2(start, count) {
            this.start = start;
            this.count = count;
        }
   
        RangeEnumerable2.prototype.getEnumerator = function () {
            return new RangeEnumerator2(this.start, this.count);
        };
   
        return RangeEnumerable2;
   
    }(Enumerable2));
   
    var RangeEnumerator2 = (function (super_) {
        inherits(RangeEnumerator2, super_);
   
        function RangeEnumerator2(start, count) {
            this.current = start - 1;
            this.end = start + count - 1;
        }
   
        RangeEnumerator2.prototype._moveNext = function () {
            if (this.current < this.end) {
                this.current = this.current + 1;
                return true;
            } else {
                return false;
            }
        };
   
        return RangeEnumerator2;
    }(Enumerator2));
};
</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
Function based
var logs = [];
var e1 = Enumerable1
    .range(0, 1000)
    .map(function (x, i) { return x * x + i; })
    .filter(function (x) { return x % 2 === 0; })
    .forEach(function (x) {
        logs.push(x);
    });
pending…
Class based
var logs = [];
var e1 = Enumerable2
    .range(0, 1000)
    .map(function (x, i) { return x * x + i; })
    .filter(function (x) { return x % 2 === 0; })
    .forEach(function (x) {
        logs.push(x);
    });
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

Marc Brooks (@IDisposable) (revision owner) commented :

Just wanted to see if swapping the order of defining things would change the lexical lookup speed. It didn't

Add a comment