summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/benchmark.pl
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/benchmark.pl')
-rwxr-xr-xapps/plugins/puzzles/src/benchmark.pl197
1 files changed, 197 insertions, 0 deletions
diff --git a/apps/plugins/puzzles/src/benchmark.pl b/apps/plugins/puzzles/src/benchmark.pl
new file mode 100755
index 0000000000..98763859e8
--- /dev/null
+++ b/apps/plugins/puzzles/src/benchmark.pl
@@ -0,0 +1,197 @@
1#!/usr/bin/perl
2
3# Process the raw output from benchmark.sh into Javascript-ified HTML.
4
5use strict;
6use warnings;
7
8my @presets = ();
9my %presets = ();
10my $maxval = 0;
11
12while (<>) {
13 chomp;
14 if (/^(.*)(#.*): ([\d\.]+)$/) {
15 push @presets, $1 unless defined $presets{$1};
16 push @{$presets{$1}}, $3;
17 $maxval = $3 if $maxval < $3;
18 }
19}
20
21print <<EOF;
22<!DOCTYPE html>
23<html>
24<head>
25<meta http-equiv="Content-Type" content="text/html; charset=ASCII" />
26<title>Puzzle generation-time benchmarks</title>
27<script type="text/javascript">
28//<![CDATA[
29function choose_scale_ticks(scale) {
30 var nscale = 1, j = 0, factors = [2,2.5,2];
31 while (scale / nscale > 20) {
32 nscale *= factors[j];
33 j = (j+1) % factors.length;
34 }
35 return nscale;
36}
37function initPlots() {
38 var canvases = document.getElementsByTagName('canvas');
39 for (var i = 0; i < canvases.length; i++) {
40 var canvas = canvases[i];
41 var scale = eval(canvas.getAttribute("data-scale"));
42 var add = 20.5, mult = (canvas.width - 2*add) / scale;
43 var data = eval(canvas.getAttribute("data-points"));
44 var ctx = canvas.getContext('2d');
45 ctx.lineWidth = '1px';
46 ctx.lineCap = 'round';
47 ctx.lineJoin = 'round';
48 ctx.strokeStyle = ctx.fillStyle = '#000000';
49 if (data === "scale") {
50 // Draw scale.
51 ctx.font = "16px sans-serif";
52 ctx.textAlign = "center";
53 ctx.textBaseline = "alphabetic";
54 var nscale = choose_scale_ticks(scale);
55 for (var x = 0; x <= scale; x += nscale) {
56 ctx.beginPath();
57 ctx.moveTo(add+mult*x, canvas.height);
58 ctx.lineTo(add+mult*x, canvas.height - 3);
59 ctx.stroke();
60 ctx.fillText(x + "s", add+mult*x, canvas.height - 6);
61 }
62 } else {
63 // Draw a box plot.
64 function quantile(x) {
65 var n = (data.length * x) | 0;
66 return (data[n-1] + data[n]) / 2;
67 }
68
69 var q1 = quantile(0.25), q2 = quantile(0.5), q3 = quantile(0.75);
70 var iqr = q3 - q1;
71 var top = 0.5, bot = canvas.height - 1.5, mid = (top+bot)/2;
72 var wlo = null, whi = null; // whisker ends
73
74 ctx.strokeStyle = '#bbbbbb';
75 var nscale = choose_scale_ticks(scale);
76 for (var x = 0; x <= scale; x += nscale) {
77 ctx.beginPath();
78 ctx.moveTo(add+mult*x, 0);
79 ctx.lineTo(add+mult*x, canvas.height);
80 ctx.stroke();
81 }
82 ctx.strokeStyle = '#000000';
83
84 for (var j in data) {
85 var x = data[j];
86 if (x >= q1 - 1.5 * iqr && x <= q3 + 1.5 * iqr) {
87 if (wlo === null || wlo > x)
88 wlo = x;
89 if (whi === null || whi < x)
90 whi = x;
91 } else {
92 ctx.beginPath();
93 ctx.arc(add+mult*x, mid, 2, 0, 2*Math.PI);
94 ctx.stroke();
95 if (x >= q1 - 3 * iqr && x <= q3 + 3 * iqr)
96 ctx.fill();
97 }
98 }
99
100 ctx.beginPath();
101
102 // Box
103 ctx.moveTo(add+mult*q1, top);
104 ctx.lineTo(add+mult*q3, top);
105 ctx.lineTo(add+mult*q3, bot);
106 ctx.lineTo(add+mult*q1, bot);
107 ctx.closePath();
108
109 // Line at median
110 ctx.moveTo(add+mult*q2, top);
111 ctx.lineTo(add+mult*q2, bot);
112
113 // Lower whisker
114 ctx.moveTo(add+mult*q1, mid);
115 ctx.lineTo(add+mult*wlo, mid);
116 ctx.moveTo(add+mult*wlo, top);
117 ctx.lineTo(add+mult*wlo, bot);
118
119 // Upper whisker
120 ctx.moveTo(add+mult*q3, mid);
121 ctx.lineTo(add+mult*whi, mid);
122 ctx.moveTo(add+mult*whi, top);
123 ctx.lineTo(add+mult*whi, bot);
124
125 ctx.stroke();
126 }
127 }
128 document.getElementById('sort_orig').onclick = function() {
129 sort(function(e) {
130 return parseFloat(e.getAttribute("data-index"));
131 });
132 };
133 document.getElementById('sort_median').onclick = function() {
134 sort(function(e) {
135 return -parseFloat(e.getAttribute("data-median"));
136 });
137 };
138 document.getElementById('sort_mean').onclick = function() {
139 sort(function(e) {
140 return -parseFloat(e.getAttribute("data-mean"));
141 });
142 };
143}
144function sort(keyfn) {
145 var rows = document.getElementsByTagName("tr");
146 var trs = [];
147 for (var i = 0; i < rows.length; i++)
148 trs.push(rows[i]);
149 trs.sort(function(a,b) {
150 var akey = keyfn(a);
151 var bkey = keyfn(b);
152 return akey < bkey ? -1 : akey > bkey ? +1 : 0;
153 });
154 var parent = trs[0].parentElement;
155 for (var i = 0; i < trs.length; i++)
156 parent.removeChild(trs[i]);
157 for (var i = 0; i < trs.length; i++)
158 parent.appendChild(trs[i]);
159}
160//]]>
161</script>
162</head>
163<body onLoad="initPlots();">
164<h1 align=center>Puzzle generation-time benchmarks</h1>
165<p>Sort order:
166<button id="sort_orig">Original</button>
167<button id="sort_median">Median</button>
168<button id="sort_mean">Mean</button>
169<table>
170<tr><th>Preset</th><td><canvas width=700 height=30 data-points='"scale"' data-scale="$maxval"></td></tr>
171EOF
172
173my $index = 0;
174for my $preset (@presets) {
175 my @data = sort { $a <=> $b } @{$presets{$preset}};
176 my $median = ($#data % 2 ?
177 ($data[($#data-1)/2]+$data[($#data+1)/2])/2 :
178 $data[$#data/2]);
179 my $mean = 0; map { $mean += $_ } @data; $mean /= @data;
180 print "<tr data-index=\"$index\" data-mean=\"$mean\" data-median=\"$median\"><td>", &escape($preset), "</td><td><canvas width=700 height=15 data-points=\"[";
181 print join ",", @data;
182 print "]\" data-scale=\"$maxval\"></td></tr>\n";
183 $index++;
184}
185
186print <<EOF;
187</body>
188</html>
189EOF
190
191sub escape {
192 my ($text) = @_;
193 $text =~ s/&/&amp;/g;
194 $text =~ s/</&lt;/g;
195 $text =~ s/>/&gt;/g;
196 return $text;
197}