c3b5c2131a
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
324 lines
11 KiB
HTML
324 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>PEG.js Benchmark Suite</title>
|
|
<style>
|
|
body {
|
|
margin: 2em 4em;
|
|
font-family: "Trebuchet MS", lucida, sans-serif;
|
|
line-height: 1.5;
|
|
color: black; background-color: white;
|
|
}
|
|
|
|
h1 { margin: 0 0 .75em 0; text-align: center; font-size: 200%; }
|
|
|
|
table { width: 48em; margin: 0 auto; border-spacing: 0; border-collapse: collapse; }
|
|
table td, table th { border: 1px solid silver; padding: .25em 1em; }
|
|
table td .unit { font-size: 75%; color: gray; }
|
|
table td.no-results { padding: 1em; text-align: center; }
|
|
table td.input-size { text-align: right; }
|
|
table td.parse-time { text-align: right; }
|
|
table td.parse-speed { text-align: right; }
|
|
table tr.columns th { border-color: #404040; color: white; background-color: #404040; }
|
|
table tr.heading th { background-color: #e0e0e0; }
|
|
table tr.benchmark-total td { font-weight: bold; }
|
|
table tr.total td { background-color: #ffff80; font-weight: bold; }
|
|
table tr.total td .unit { color: #808000; }
|
|
table tr.total td.parse-speed .value { font-size: 175%; }
|
|
|
|
a, a:visited { color: #3d586c; }
|
|
|
|
#options {
|
|
width: 46em;
|
|
margin: 2em auto; border-radius: .5em; -moz-border-radius: .5em; padding: .5em 1em;
|
|
text-align: center;
|
|
background-color: #f0f0f0;
|
|
}
|
|
#options #run-count { width: 3em; }
|
|
#options #run { width: 5em; margin-left: 2em; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>PEG.js Benchmark Suite</h1>
|
|
|
|
<div id="options">
|
|
<label for="run-count">Run each test</label>
|
|
<input type="text" id="run-count" value="10"> times
|
|
<input type="button" id="run" value="Run">
|
|
</div>
|
|
|
|
<table id="results-table">
|
|
<tr class="columns">
|
|
<th>Test</th>
|
|
<th>Input Size</th>
|
|
<th>Average Parse Time</th>
|
|
<th>Average Parse Speed</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="no-results" colspan="4">No results available yet.</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<script src="../lib/compiler.js"></script>
|
|
<script src="../lib/metagrammar.js"></script>
|
|
<script src="../vendor/jquery/jquery.js"></script>
|
|
<script src="../vendor/jquery.scrollto/jquery.scrollTo.js"></script>
|
|
<script>
|
|
var benchmarks = [
|
|
{
|
|
id: "json",
|
|
title: "JSON",
|
|
tests: [
|
|
{ file: "example1.json", title: "Example 1" },
|
|
{ file: "example2.json", title: "Example 2" },
|
|
{ file: "example3.json", title: "Example 3" },
|
|
{ file: "example4.json", title: "Example 4" },
|
|
{ file: "example5.json", title: "Example 5" }
|
|
]
|
|
},
|
|
{
|
|
id: "css",
|
|
title: "CSS",
|
|
tests: [
|
|
{ file: "blueprint/src/reset.css", title: "Blueprint - reset.css (source)" },
|
|
{ file: "blueprint/src/typography.css", title: "Blueprint - typography.css (source)" },
|
|
{ file: "blueprint/src/forms.css", title: "Blueprint - forms.css (source)" },
|
|
{ file: "blueprint/src/grid.css", title: "Blueprint - grid.css (source)" },
|
|
{ file: "blueprint/src/print.css", title: "Blueprint - print.css (source)" },
|
|
// Contains syntax errors.
|
|
// { file: "blueprint/src/ie.css", title: "Blueprint - ie.css (source)" },
|
|
{ file: "blueprint/min/screen.css", title: "Blueprint - screen.css (minified)" },
|
|
{ file: "blueprint/min/print.css", title: "Blueprint - print.css (minified)" },
|
|
// Contains syntax errors.
|
|
// { file: "blueprint/min/ie.css", title: "Blueprint - ie.css (minified)" },
|
|
{ file: "960.gs/src/reset.css", title: "960.gs - reset.css (source)" },
|
|
{ file: "960.gs/src/text.css", title: "960.gs - text.css (source)" },
|
|
{ file: "960.gs/src/960.css", title: "960.gs - 960.css (source)" },
|
|
{ file: "960.gs/src/960_24_col.css", title: "960.gs - 960_24_col.css (source)" },
|
|
{ file: "960.gs/min/reset.css", title: "960.gs - reset.css (minified)" },
|
|
{ file: "960.gs/min/text.css", title: "960.gs - text.css (minified)" },
|
|
{ file: "960.gs/min/960.css", title: "960.gs - 960.css (minified)" },
|
|
{ file: "960.gs/min/960_24_col.css", title: "960.gs - 960_24_col.css (minified)" }
|
|
]
|
|
}
|
|
];
|
|
|
|
$("#run").click(function() {
|
|
|
|
/* Results Table Manipulation */
|
|
|
|
var resultsTable = $("#results-table");
|
|
|
|
function appendHeading(heading) {
|
|
resultsTable.append(
|
|
"<tr class='heading'><th colspan='4'>" + heading + "</th></tr>"
|
|
);
|
|
}
|
|
|
|
function appendResult(klass, title, url, inputSize, parseTime) {
|
|
var KB = 1024;
|
|
var MS_IN_S = 1000;
|
|
|
|
resultsTable.append(
|
|
"<tr class='" + klass + "'>"
|
|
+ "<td class='title'>"
|
|
+ (url !== null ? "<a href='" + url + "'>" : "")
|
|
+ title
|
|
+ (url !== null ? "</a>" : "")
|
|
+ "</td>"
|
|
+ "<td class='input-size'>"
|
|
+ "<span class='value'>"
|
|
+ (inputSize / KB).toFixed(2)
|
|
+ "</span>"
|
|
+ " <span class='unit'>kB</span>"
|
|
+ "</td>"
|
|
+ "<td class='parse-time'>"
|
|
+ "<span class='value'>"
|
|
+ parseTime.toFixed(2)
|
|
+ "</span>"
|
|
+ " <span class='unit'>ms</span>"
|
|
+ "</td>"
|
|
+ "<td class='parse-speed'>"
|
|
+ "<span class='value'>"
|
|
+ ((inputSize / KB) / (parseTime / MS_IN_S)).toFixed(2)
|
|
+ "</span>"
|
|
+ " <span class='unit'>kB/s</span>"
|
|
+ "</td>"
|
|
+ "</tr>"
|
|
);
|
|
}
|
|
|
|
/* AJAX */
|
|
|
|
function get(url) {
|
|
return $.ajax({
|
|
type: "GET",
|
|
url: url,
|
|
dataType: "text",
|
|
async: false
|
|
}).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:
|
|
*
|
|
* 1. To warm up the interpreter (PEG.js-generated parsers will be
|
|
* most likely used repeatedly, so it makes sense to measure
|
|
* performance after warming up).
|
|
*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* 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");
|
|
|
|
resultsTable.show();
|
|
$("#results-table tr").slice(1).remove();
|
|
|
|
state.totalInputSize = 0;
|
|
state.totalParseTime = 0;
|
|
}
|
|
|
|
function benchmarkInitializer(i) {
|
|
return function() {
|
|
var benchmark = benchmarks[i];
|
|
|
|
appendHeading(benchmark.title);
|
|
|
|
var grammar = get("../examples/" + benchmark.id + ".pegjs");
|
|
state.parser = PEG.buildParser(grammar);
|
|
|
|
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;
|
|
|
|
var input = get(url);
|
|
|
|
var parseTime = 0;
|
|
for (var k = 0; k < runCount; k++) {
|
|
var t = (new Date).getTime();
|
|
state.parser.parse(input);
|
|
parseTime += (new Date).getTime() - t;
|
|
}
|
|
var averageParseTime = parseTime / runCount;
|
|
|
|
appendResult(
|
|
"individual",
|
|
test.title,
|
|
url,
|
|
input.length,
|
|
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(
|
|
"total",
|
|
"Total",
|
|
null,
|
|
state.totalInputSize,
|
|
state.totalParseTime
|
|
);
|
|
|
|
$.scrollTo("max", { axis: "y", duration: 500 });
|
|
|
|
$("#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() {
|
|
$("#run").focus();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|