chiark / gitweb /
Introduce some extra testing and benchmarking command-line options to
[sgt-puzzles.git] / benchmark.pl
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5
6 my @presets = ();
7 my %presets = ();
8 my $maxval = 0;
9
10 while (<>) {
11     chomp;
12     if (/^(.*)(#.*): ([\d\.]+)$/) {
13         push @presets, $1 unless defined $presets{$1};
14         push @{$presets{$1}}, $3;
15         $maxval = $3 if $maxval < $3;
16     }
17 }
18
19 print <<EOF;
20 <!DOCTYPE html>
21 <html>
22 <head>
23 <meta http-equiv="Content-Type" content="text/html; charset=ASCII" />
24 <title>Puzzle generation-time benchmarks</title>
25 <script type="text/javascript">
26 //<![CDATA[
27 function choose_scale_ticks(scale) {
28     var nscale = 1, j = 0, factors = [2,2.5,2];
29     while (scale / nscale > 20) {
30         nscale *= factors[j];
31         j = (j+1) % factors.length;
32     } 
33     return nscale;
34 }
35 function initPlots() {
36     var canvases = document.getElementsByTagName('canvas');
37     for (var i = 0; i < canvases.length; i++) {
38         var canvas = canvases[i];
39         var scale = eval(canvas.getAttribute("data-scale"));
40         var add = 20.5, mult = (canvas.width - 2*add) / scale;
41         var data = eval(canvas.getAttribute("data-points"));
42         var ctx = canvas.getContext('2d');
43         ctx.lineWidth = '1px';
44         ctx.lineCap = 'round';
45         ctx.lineJoin = 'round';
46         ctx.strokeStyle = ctx.fillStyle = '#000000';
47         if (data === "scale") {
48             // Draw scale.
49             ctx.font = "16px sans-serif";
50             ctx.textAlign = "center";
51             ctx.textBaseline = "alphabetic";
52             var nscale = choose_scale_ticks(scale);
53             for (var x = 0; x <= scale; x += nscale) {
54                 ctx.beginPath();
55                 ctx.moveTo(add+mult*x, canvas.height);
56                 ctx.lineTo(add+mult*x, canvas.height - 3);
57                 ctx.stroke();
58                 ctx.fillText(x + "s", add+mult*x, canvas.height - 6);
59             }
60         } else {
61             // Draw a box plot.
62             function quantile(x) {
63                 var n = (data.length * x) | 0;
64                 return (data[n-1] + data[n]) / 2;
65             }
66
67             var q1 = quantile(0.25), q2 = quantile(0.5), q3 = quantile(0.75);
68             var iqr = q3 - q1;
69             var top = 0.5, bot = canvas.height - 1.5, mid = (top+bot)/2;
70             var wlo = null, whi = null; // whisker ends
71
72             ctx.strokeStyle = '#bbbbbb';
73             var nscale = choose_scale_ticks(scale);
74             for (var x = 0; x <= scale; x += nscale) {
75                 ctx.beginPath();
76                 ctx.moveTo(add+mult*x, 0);
77                 ctx.lineTo(add+mult*x, canvas.height);
78                 ctx.stroke();
79             }
80             ctx.strokeStyle = '#000000';
81
82             for (var j in data) {
83                 var x = data[j];
84                 if (x >= q1 - 1.5 * iqr && x <= q3 + 1.5 * iqr) {
85                     if (wlo === null || wlo > x)
86                         wlo = x;
87                     if (whi === null || whi < x)
88                         whi = x;
89                 } else {
90                     ctx.beginPath();
91                     ctx.arc(add+mult*x, mid, 2, 0, 2*Math.PI);
92                     ctx.stroke();
93                     if (x >= q1 - 3 * iqr && x <= q3 + 3 * iqr)
94                         ctx.fill();
95                 }
96             }
97
98             ctx.beginPath();
99
100             // Box
101             ctx.moveTo(add+mult*q1, top);
102             ctx.lineTo(add+mult*q3, top);
103             ctx.lineTo(add+mult*q3, bot);
104             ctx.lineTo(add+mult*q1, bot);
105             ctx.closePath();
106
107             // Line at median
108             ctx.moveTo(add+mult*q2, top);
109             ctx.lineTo(add+mult*q2, bot);
110
111             // Lower whisker
112             ctx.moveTo(add+mult*q1, mid);
113             ctx.lineTo(add+mult*wlo, mid);
114             ctx.moveTo(add+mult*wlo, top);
115             ctx.lineTo(add+mult*wlo, bot);
116
117             // Upper whisker
118             ctx.moveTo(add+mult*q3, mid);
119             ctx.lineTo(add+mult*whi, mid);
120             ctx.moveTo(add+mult*whi, top);
121             ctx.lineTo(add+mult*whi, bot);
122
123             ctx.stroke();
124         }
125     }
126 }
127 //]]>
128 </script>
129 </head>
130 <body onLoad="initPlots();">
131 <h1 align=center>Puzzle generation-time benchmarks</h1>
132 <table>
133 <tr><th>Preset</th><td><canvas width=700 height=30 data-points='"scale"' data-scale="$maxval"></td></tr>
134 EOF
135
136 for my $preset (@presets) {
137     print "<tr><td>", &escape($preset), "</td><td><canvas width=700 height=15 data-points=\"[";
138     print join ",", sort { $a <=> $b } @{$presets{$preset}};
139     print "]\" data-scale=\"$maxval\"></td></tr>\n";
140 }
141
142 print <<EOF;
143 </body>
144 </html>
145 EOF
146
147 sub escape {
148     my ($text) = @_;
149     $text =~ s/&/&amp;/g;
150     $text =~ s/</&lt;/g;
151     $text =~ s/>/&gt;/g;
152     return $text;
153 }