From c3b5c2131af12b6710b10a5d71d65603002d716f Mon Sep 17 00:00:00 2001 From: David Majda Date: Sat, 24 Jul 2010 14:48:12 +0200 Subject: [PATCH] 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 --- benchmark/index.html | 134 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 107 insertions(+), 27 deletions(-) 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() {