Spaces:
Sleeping
Sleeping
| #!/usr/bin/perl | |
| # Process the raw output from benchmark.sh into Javascript-ified HTML. | |
| use strict; | |
| use warnings; | |
| my @presets = (); | |
| my %presets = (); | |
| my $maxval = 0; | |
| while (<<>>) { | |
| chomp; | |
| if (/^(.*)(#.*): ([\d\.]+)$/) { | |
| push @presets, $1 unless defined $presets{$1}; | |
| push @{$presets{$1}}, $3; | |
| $maxval = $3 if $maxval < $3; | |
| } | |
| } | |
| print <<EOF; | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta http-equiv="Content-Type" content="text/html; charset=ASCII" /> | |
| <title>Puzzle generation-time benchmarks</title> | |
| <script type="text/javascript"> | |
| //<![CDATA[ | |
| function choose_scale_ticks(scale) { | |
| var nscale = 1, j = 0, factors = [2,2.5,2]; | |
| while (scale / nscale > 20) { | |
| nscale *= factors[j]; | |
| j = (j+1) % factors.length; | |
| } | |
| return nscale; | |
| } | |
| function initPlots() { | |
| var canvases = document.getElementsByTagName('canvas'); | |
| for (var i = 0; i < canvases.length; i++) { | |
| var canvas = canvases[i]; | |
| var scale = eval(canvas.getAttribute("data-scale")); | |
| var add = 20.5, mult = (canvas.width - 2*add) / scale; | |
| var data = eval(canvas.getAttribute("data-points")); | |
| var ctx = canvas.getContext('2d'); | |
| ctx.lineWidth = '1px'; | |
| ctx.lineCap = 'round'; | |
| ctx.lineJoin = 'round'; | |
| ctx.strokeStyle = ctx.fillStyle = '#000000'; | |
| if (data === "scale") { | |
| // Draw scale. | |
| ctx.font = "16px sans-serif"; | |
| ctx.textAlign = "center"; | |
| ctx.textBaseline = "alphabetic"; | |
| var nscale = choose_scale_ticks(scale); | |
| for (var x = 0; x <= scale; x += nscale) { | |
| ctx.beginPath(); | |
| ctx.moveTo(add+mult*x, canvas.height); | |
| ctx.lineTo(add+mult*x, canvas.height - 3); | |
| ctx.stroke(); | |
| ctx.fillText(x + "s", add+mult*x, canvas.height - 6); | |
| } | |
| } else { | |
| // Draw a box plot. | |
| function quantile(x) { | |
| var n = (data.length * x) | 0; | |
| return (data[n-1] + data[n]) / 2; | |
| } | |
| var q1 = quantile(0.25), q2 = quantile(0.5), q3 = quantile(0.75); | |
| var iqr = q3 - q1; | |
| var top = 0.5, bot = canvas.height - 1.5, mid = (top+bot)/2; | |
| var wlo = null, whi = null; // whisker ends | |
| ctx.strokeStyle = '#bbbbbb'; | |
| var nscale = choose_scale_ticks(scale); | |
| for (var x = 0; x <= scale; x += nscale) { | |
| ctx.beginPath(); | |
| ctx.moveTo(add+mult*x, 0); | |
| ctx.lineTo(add+mult*x, canvas.height); | |
| ctx.stroke(); | |
| } | |
| ctx.strokeStyle = '#000000'; | |
| for (var j in data) { | |
| var x = data[j]; | |
| if (x >= q1 - 1.5 * iqr && x <= q3 + 1.5 * iqr) { | |
| if (wlo === null || wlo > x) | |
| wlo = x; | |
| if (whi === null || whi < x) | |
| whi = x; | |
| } else { | |
| ctx.beginPath(); | |
| ctx.arc(add+mult*x, mid, 2, 0, 2*Math.PI); | |
| ctx.stroke(); | |
| if (x >= q1 - 3 * iqr && x <= q3 + 3 * iqr) | |
| ctx.fill(); | |
| } | |
| } | |
| ctx.beginPath(); | |
| // Box | |
| ctx.moveTo(add+mult*q1, top); | |
| ctx.lineTo(add+mult*q3, top); | |
| ctx.lineTo(add+mult*q3, bot); | |
| ctx.lineTo(add+mult*q1, bot); | |
| ctx.closePath(); | |
| // Line at median | |
| ctx.moveTo(add+mult*q2, top); | |
| ctx.lineTo(add+mult*q2, bot); | |
| // Lower whisker | |
| ctx.moveTo(add+mult*q1, mid); | |
| ctx.lineTo(add+mult*wlo, mid); | |
| ctx.moveTo(add+mult*wlo, top); | |
| ctx.lineTo(add+mult*wlo, bot); | |
| // Upper whisker | |
| ctx.moveTo(add+mult*q3, mid); | |
| ctx.lineTo(add+mult*whi, mid); | |
| ctx.moveTo(add+mult*whi, top); | |
| ctx.lineTo(add+mult*whi, bot); | |
| ctx.stroke(); | |
| } | |
| } | |
| document.getElementById('sort_orig').onclick = function() { | |
| sort(function(e) { | |
| return parseFloat(e.getAttribute("data-index")); | |
| }); | |
| }; | |
| document.getElementById('sort_median').onclick = function() { | |
| sort(function(e) { | |
| return -parseFloat(e.getAttribute("data-median")); | |
| }); | |
| }; | |
| document.getElementById('sort_mean').onclick = function() { | |
| sort(function(e) { | |
| return -parseFloat(e.getAttribute("data-mean")); | |
| }); | |
| }; | |
| } | |
| function sort(keyfn) { | |
| var rows = document.getElementsByTagName("tr"); | |
| var trs = []; | |
| for (var i = 0; i < rows.length; i++) | |
| trs.push(rows[i]); | |
| trs.sort(function(a,b) { | |
| var akey = keyfn(a); | |
| var bkey = keyfn(b); | |
| return akey < bkey ? -1 : akey > bkey ? +1 : 0; | |
| }); | |
| var parent = trs[0].parentElement; | |
| for (var i = 0; i < trs.length; i++) | |
| parent.removeChild(trs[i]); | |
| for (var i = 0; i < trs.length; i++) | |
| parent.appendChild(trs[i]); | |
| } | |
| //]]> | |
| </script> | |
| </head> | |
| <body onLoad="initPlots();"> | |
| <h1 align=center>Puzzle generation-time benchmarks</h1> | |
| <p>Sort order: | |
| <button id="sort_orig">Original</button> | |
| <button id="sort_median">Median</button> | |
| <button id="sort_mean">Mean</button> | |
| <table> | |
| <tr><th>Preset</th><td><canvas width=700 height=30 data-points='"scale"' data-scale="$maxval"></td></tr> | |
| EOF | |
| my $index = 0; | |
| for my $preset (@presets) { | |
| my @data = sort { $a <=> $b } @{$presets{$preset}}; | |
| my $median = ($#data % 2 ? | |
| ($data[($#data-1)/2]+$data[($#data+1)/2])/2 : | |
| $data[$#data/2]); | |
| my $mean = 0; map { $mean += $_ } @data; $mean /= @data; | |
| print "<tr data-index=\"$index\" data-mean=\"$mean\" data-median=\"$median\"><td>", &escape($preset), "</td><td><canvas width=700 height=15 data-points=\"["; | |
| print join ",", @data; | |
| print "]\" data-scale=\"$maxval\"></td></tr>\n"; | |
| $index++; | |
| } | |
| print <<EOF; | |
| </body> | |
| </html> | |
| EOF | |
| sub escape { | |
| my ($text) = @_; | |
| $text =~ s/&/&/g; | |
| $text =~ s/</</g; | |
| $text =~ s/>/>/g; | |
| return $text; | |
| } | |