02 July, 2009

Timer constructor.

"use strict";
var Timer = function (iter) {
    function manip(item) {
        return item;
    }
    this.average = function (func) {
        manip = func;
    };
    this.results = function () {
        var i, length = this.length, times = [];
        for (i = 0; i < length; i += 1) {
            times.push(manip(Timer.run(this[i], iter), iter));
        }
        return times;
    };
};
Timer.prototype = [];
Timer.run = function (func, length) {
    var i, start, end;
    length = length || 1;
    start = new Date();
    for (i = 0; i < length; i += 1) {
        func();
    }
    end = new Date();
    return end - start;
};

In an earlier post I described a simple function for benchmark testing in JavaScript. There isn't anything wrong with this function (aside from it being a global), however it doesn't do very much.

Most of the time, when I want to do benchmarks, I'm comparing 2+ ways of doing the same thing. This allows me to pick whichever method is the fastest. It is possible with getTime, but it is cumbersome. I then set out to create the Timer constructor, to alleviate this problem.

You initialize it with new Timer(), and you can pass in an optional number, indicating the iterations. If you create it with new Timer(1000), then every function will be called 1,000 times.

Timer is similar to an array of functions. You can add new functions with the push() method:

var timer = new Timer(100);
timer.push(function () {
    /* code goes here! */
});

You can use push() to add as many functions as you like. In order to obtain the actual time it takes to run the functions, you call the results() method, which returns an array:

// An array of benchmarks:
timer.results();

You can then call join() to display the array in various ways:

// Use custom separators:
timer.results().join("");
timer.results().join("\n");
timer.results().join(" + ");

Lastly, there's the average() method, which allows you to manipulate the benchmark. For instance, to average the results based on the mean:

timer.average(function (item, iter) {
    return item / iter;
});

You pass in a function, which is run after each benchmark has been computed. The first argument is how long it took to run the function, and the second argument is how many times the function was run (iterations). Whatever the function returns is used instead of the normal time.

Using these combined, you can create a simple script to compare two or more pieces of code, to determine which is fastest:

var timer = new Timer(10000);

//-- Begin benchmark functions
timer.push(function () {
    $("#test").css("backgroundColor", "black");
});

timer.push(function () {
    $("#test").css({
        backgroundColor: "black"
    });
});

timer.push(function () {
    $("#test").attr("style", "background-color: black;");
});
//-- End benchmark functions

timer.average(function (item, iter) {
    return item / iter;
});
alert(timer.results().join("\n"));

The above runs three functions (that do the same thing), computes how long it takes to run them 10,000 times, averages the result, and lastly displays it. If you don't want it to average the result, simply leave out the timer.average() call.

Finally, you can still access the old getTime under the name Timer.run:

var time = Timer.run(function () {
    /* code goes here! */
}, 100000);

[LINK] The source code.
[LINK] The unit tests.

No comments:

Post a Comment