diff options
Diffstat (limited to 'apps/plugins/puzzles/src/benchmark.pl')
-rwxr-xr-x | apps/plugins/puzzles/src/benchmark.pl | 197 |
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 | |||
5 | use strict; | ||
6 | use warnings; | ||
7 | |||
8 | my @presets = (); | ||
9 | my %presets = (); | ||
10 | my $maxval = 0; | ||
11 | |||
12 | while (<>) { | ||
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 | |||
21 | print <<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[ | ||
29 | function 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 | } | ||
37 | function 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 | } | ||
144 | function 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> | ||
171 | EOF | ||
172 | |||
173 | my $index = 0; | ||
174 | for 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 | |||
186 | print <<EOF; | ||
187 | </body> | ||
188 | </html> | ||
189 | EOF | ||
190 | |||
191 | sub escape { | ||
192 | my ($text) = @_; | ||
193 | $text =~ s/&/&/g; | ||
194 | $text =~ s/</</g; | ||
195 | $text =~ s/>/>/g; | ||
196 | return $text; | ||
197 | } | ||