|
|
|
<!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/peg.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>
|