Repeated calling lists of functions

JavaScript performance comparison

Test case created

Info

We are doing some heavy iterative work and we need to allow callers to set up custom "pipelines" of functions which will all be executed in order every loop. Each function will be executed hundreds of times. What is the performance hit of providing a nice dynamic API such as EventEmitter for registering the functions, over asking them to just define the functions they want to call and in what order in a custom callback.

Preparation code

<script src="https://raw.github.com/Wolfy87/EventEmitter/master/EventEmitter.min.js">
</script>
<script>
Benchmark.prototype.setup = function() {
    REPS = 300;
   
    do1 = do2 = do3 = do4 = function(arg) {
      // Sample work
      arg * 5;
    };
   
    // First implementation, provides only an onEnter method which
    // must be called every loop
    // "Listeners" are added manually in the required order.
    // return values must also be manually handled.
   
   
    function O1() {}
    O1.prototype.onEnter = function(arg) {
      arg = do1.call(this, arg);
      arg = do2.call(this, arg);
      arg = do3.call(this, arg);
      arg = do4.call(this, arg);
      return arg;
    };
    o1 = new O1;
   
    // Second implementation
    // (Basic EventEmitter implementation; incomplete as it only defines emit())
    // This way, functions can be registered in an Array and will be
    // invoked using an additional for loop
    // To execute the functions, we can emit("enter");
   
    function O2() {
      this.events = {};
    }
    O2.prototype.emit = function(event, arg) {
      var listeners = this.events[event];
      if (listeners) {
        for (var i = 0, l; l = listeners[i]; ++i) {
          arg = l.call(this, arg);
        }
      }
      return arg;
    };
   
    o2 = new O2;
    o2.events["enter"] = [do1, do2, do3, do4];
   
    // https://github.com/Wolfy87/EventEmitter
    // Compare with a full-blown EventEmitter implementation
    o3 = new EventEmitter;
    o3.on("enter", do1);
    o3.on("enter", do2);
    o3.on("enter", do3);
    o3.on("enter", do4);
   
    function compile(funcList) {
      var closureVars = [],
          fnName,
          funcBody = "";
     
      for (var i = 0, l = funcList.length; i < l; ++i) {
        closureVars.push((fnName = "f" + i) + ' = funcList[' + i + ']');
        funcBody += 'arg = ' + fnName + '.call(this, arg);\n';
      }
      funcBody += "return arg;";
     
      return Function("funcList",
        "var " + closureVars.join(", ") + ";" +
        "return function (arg) {" + funcBody + "};"
      )(funcList);
    }
    o4 = {events: {enter: [do1, do2, do3, do4]}};
};
</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
Old fashioned callback
for (var i = 0; i < REPS; ++i) o1.onEnter(i);
pending…
Basic for-loop event emitter
for (var i = 0; i < REPS; ++i) o2.emit("enter", i);
pending…
Full event emitter implementation
for (var i = 0; i < REPS; ++i) o3.emit("enter", i);
pending…
Dynamically unrolled loop
fn = compile(o4.events.enter);
for (var i = 0; i < REPS; ++i) fn.call(o4, i);
pending…

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

Compare results of other browsers

0 comments

Add a comment