Benchmark: Factor benchmark into several functions run using |setTimeout|

We do this for two reasons:

  1. To avoid bowser mechanism for interrupting long-running scripts to
     kick-in (or at least to not kick-in that often).

  2. To ensure progressive rendering of results in the browser (some
     browsers do not render at all when running JavaScript code).

This does not change the benchmark suite execution speed statistically
significantly on V8.

Detailed results (benchmark suite totals):

---------------------------------
 Test #     Before       After
---------------------------------
      1   31.04 kB/s   31.18 kB/s
      2   31.26 kB/s   30.89 kB/s
      3   31.15 kB/s   31.19 kB/s
      4   30.52 kB/s   31.21 kB/s
      5   31.00 kB/s   30.73 kB/s
---------------------------------
Average   30.99 kB/s   31.04 kB/s
---------------------------------

Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4
redux
David Majda 14 years ago
parent 647e7be1fd
commit c3b5c2131a

@ -105,6 +105,9 @@
]; ];
$("#run").click(function() { $("#run").click(function() {
/* Results Table Manipulation */
var resultsTable = $("#results-table"); var resultsTable = $("#results-table");
function appendHeading(heading) { function appendHeading(heading) {
@ -146,6 +149,8 @@
); );
} }
/* AJAX */
function get(url) { function get(url) {
return $.ajax({ return $.ajax({
type: "GET", type: "GET",
@ -155,6 +160,30 @@
}).responseText; }).responseText;
} }
/* Queue */
var Q = {
functions: [],
add: function(f) {
this.functions.push(f);
},
run: function() {
if (this.functions.length > 0) {
this.functions.shift()();
/*
* We can't use |arguments.callee| here because |this| would get
* messed-up in that case.
*/
setTimeout(function() { Q.run() }, 0);
}
}
};
/* Main */
/* /*
* Each input is parsed multiple times and the results are averaged. We * Each input is parsed multiple times and the results are averaged. We
* do this for two reasons: * do this for two reasons:
@ -165,32 +194,57 @@
* *
* 2. To minimize random errors. * 2. To minimize random errors.
*/ */
var runCount = parseInt($("#run-count").val()); var runCount = parseInt($("#run-count").val());
if (isNaN(runCount) || runCount <= 0) { if (isNaN(runCount) || runCount <= 0) {
alert("Number of runs must be a positive integer."); alert("Number of runs must be a positive integer.");
return; return;
} }
/*
* The benchmark itself is factored out into several functions (some of
* them generated), which are enqueued and run one by one using
* |setTimeout|. We do this for two reasons:
*
* 1. To avoid bowser mechanism for interrupting long-running scripts
* to kick-in (or at least to not kick-in that often).
*
* 2. To ensure progressive rendering of results in the browser (some
* browsers do not render at all when running JavaScript code).
*
* The enqueued functions share state, which is all stored in the
* properties of the |state| object.
*/
var state = {};
function initialize() {
$("#run-count, #run").attr("disabled", "disabled"); $("#run-count, #run").attr("disabled", "disabled");
resultsTable.show(); resultsTable.show();
$("#results-table tr").slice(1).remove(); $("#results-table tr").slice(1).remove();
var totalInputSize = 0; state.totalInputSize = 0;
var totalParseTime = 0; state.totalParseTime = 0;
}
for (var i = 0; i < benchmarks.length; i++) { function benchmarkInitializer(i) {
return function() {
var benchmark = benchmarks[i]; var benchmark = benchmarks[i];
appendHeading(benchmark.title); appendHeading(benchmark.title);
var grammar = get("../examples/" + benchmark.id + ".pegjs"); var grammar = get("../examples/" + benchmark.id + ".pegjs");
var parser = PEG.buildParser(grammar); state.parser = PEG.buildParser(grammar);
var benchmarkInputSize = 0; state.benchmarkInputSize = 0;
var benchmarkParseTime = 0; state.benchmarkParseTime = 0;
};
}
for (var j = 0; j < benchmark.tests.length; j++) { function testRunner(i, j) {
return function() {
var benchmark = benchmarks[i];
var test = benchmark.tests[j]; var test = benchmark.tests[j];
var url = benchmark.id + "/" + test.file; var url = benchmark.id + "/" + test.file;
@ -199,7 +253,7 @@
var parseTime = 0; var parseTime = 0;
for (var k = 0; k < runCount; k++) { for (var k = 0; k < runCount; k++) {
var t = (new Date).getTime(); var t = (new Date).getTime();
parser.parse(input); state.parser.parse(input);
parseTime += (new Date).getTime() - t; parseTime += (new Date).getTime() - t;
} }
var averageParseTime = parseTime / runCount; var averageParseTime = parseTime / runCount;
@ -212,27 +266,53 @@
averageParseTime averageParseTime
); );
benchmarkInputSize += input.length; state.benchmarkInputSize += input.length;
benchmarkParseTime += averageParseTime; state.benchmarkParseTime += averageParseTime;
};
} }
function benchmarkFinalizer(i) {
return function() {
var benchmark = benchmarks[i];
appendResult( appendResult(
"benchmark-total", "benchmark-total",
benchmark.title + " total", benchmark.title + " total",
null, null,
benchmarkInputSize, state.benchmarkInputSize,
benchmarkParseTime state.benchmarkParseTime
); );
totalInputSize += benchmarkInputSize; state.totalInputSize += state.benchmarkInputSize;
totalParseTime += benchmarkParseTime; state.totalParseTime += state.benchmarkParseTime;
};
} }
appendResult("total", "Total", null, totalInputSize, totalParseTime); function finalize() {
appendResult(
"total",
"Total",
null,
state.totalInputSize,
state.totalParseTime
);
$.scrollTo("max", { axis: "y", duration: 500 }); $.scrollTo("max", { axis: "y", duration: 500 });
$("#run-count, #run").removeAttr("disabled"); $("#run-count, #run").removeAttr("disabled");
}
Q.add(initialize);
for (var i = 0; i < benchmarks.length; i++) {
Q.add(benchmarkInitializer(i));
for (var j = 0; j < benchmarks[i].tests.length; j++) {
Q.add(testRunner(i, j));
}
Q.add(benchmarkFinalizer(i));
}
Q.add(finalize);
Q.run();
}); });
$(document).ready(function() { $(document).ready(function() {

Loading…
Cancel
Save