diff --git a/benchmark/index.html b/benchmark/index.html index 2733d31..3bc29a5 100644 --- a/benchmark/index.html +++ b/benchmark/index.html @@ -105,6 +105,9 @@ ]; $("#run").click(function() { + + /* Results Table Manipulation */ + var resultsTable = $("#results-table"); function appendHeading(heading) { @@ -146,6 +149,8 @@ ); } + /* AJAX */ + function get(url) { return $.ajax({ type: "GET", @@ -155,6 +160,30 @@ }).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 * do this for two reasons: @@ -165,32 +194,57 @@ * * 2. To minimize random errors. */ + var runCount = parseInt($("#run-count").val()); if (isNaN(runCount) || runCount <= 0) { alert("Number of runs must be a positive integer."); return; } - $("#run-count, #run").attr("disabled", "disabled"); + /* + * 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. + */ - resultsTable.show(); - $("#results-table tr").slice(1).remove(); + var state = {}; - var totalInputSize = 0; - var totalParseTime = 0; + function initialize() { + $("#run-count, #run").attr("disabled", "disabled"); - for (var i = 0; i < benchmarks.length; i++) { - var benchmark = benchmarks[i]; + resultsTable.show(); + $("#results-table tr").slice(1).remove(); - appendHeading(benchmark.title); + state.totalInputSize = 0; + state.totalParseTime = 0; + } + + function benchmarkInitializer(i) { + return function() { + var benchmark = benchmarks[i]; - var grammar = get("../examples/" + benchmark.id + ".pegjs"); - var parser = PEG.buildParser(grammar); + appendHeading(benchmark.title); - var benchmarkInputSize = 0; - var benchmarkParseTime = 0; + var grammar = get("../examples/" + benchmark.id + ".pegjs"); + state.parser = PEG.buildParser(grammar); - for (var j = 0; j < benchmark.tests.length; j++) { + state.benchmarkInputSize = 0; + state.benchmarkParseTime = 0; + }; + } + + function testRunner(i, j) { + return function() { + var benchmark = benchmarks[i]; var test = benchmark.tests[j]; var url = benchmark.id + "/" + test.file; @@ -199,7 +253,7 @@ var parseTime = 0; for (var k = 0; k < runCount; k++) { var t = (new Date).getTime(); - parser.parse(input); + state.parser.parse(input); parseTime += (new Date).getTime() - t; } var averageParseTime = parseTime / runCount; @@ -212,27 +266,53 @@ averageParseTime ); - benchmarkInputSize += input.length; - benchmarkParseTime += averageParseTime; - } + state.benchmarkInputSize += input.length; + state.benchmarkParseTime += averageParseTime; + }; + } + + function benchmarkFinalizer(i) { + return function() { + var benchmark = benchmarks[i]; + appendResult( + "benchmark-total", + benchmark.title + " total", + null, + state.benchmarkInputSize, + state.benchmarkParseTime + ); + + state.totalInputSize += state.benchmarkInputSize; + state.totalParseTime += state.benchmarkParseTime; + }; + } + + function finalize() { appendResult( - "benchmark-total", - benchmark.title + " total", + "total", + "Total", null, - benchmarkInputSize, - benchmarkParseTime + state.totalInputSize, + state.totalParseTime ); - totalInputSize += benchmarkInputSize; - totalParseTime += benchmarkParseTime; - } + $.scrollTo("max", { axis: "y", duration: 500 }); - appendResult("total", "Total", null, totalInputSize, totalParseTime); + $("#run-count, #run").removeAttr("disabled"); + } - $.scrollTo("max", { axis: "y", duration: 500 }); + 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); - $("#run-count, #run").removeAttr("disabled"); + Q.run(); }); $(document).ready(function() {