sub speed_report
{
    my %log;
    my %rpt = ( speed => { min => 9999999, max => 0 });
    find (sub {
	open my $rpt, "<", $_ or return;
	my $host;
	my $cc;
	while (<$rpt>) {
	    chomp;
	    m/^(\w+):/				and      $host            //= $1;
	    m/^\s+on\s+(.*)/			and $log{$host}{os}       //= $1;
	    m/^\s+using\s+(?:ccache\s+)?(\S+)/	and      $cc              //=
							$1 eq "g++" ? "gcc" :
							$1 eq "xlc" ?  "cc" : $1;
	    m/^Summary:\s+(.*)/			and $log{$host}{$cc}{sum} //= $1;
	    m/^(v5\S+)\s+Configuration\s*(.*)/	and $log{$host}{$cc}{prl} //= $1
						and $log{$host}{$cc}{cnf} //= $2;
	    m/^((?:\w \w ){2,3})\s*(.*?)\s*$/	and $log{$host}{$cc}{ofs}{$2} = $1;
	    m/^(?:\| )*\+-+ [A-Z_]+\s*=\s*(.*)/	and unshift @{$log{$host}{$cc}{tst}}, $1;
	    }
	close $rpt;
	$log{$host}{$cc}{prl} =~ s/-g[0-9a-f]+$//;
	$log{$host}{$cc}{cnf} =~ s/\(common\)\s//;

	my ($conf, $env, $dbg);
	open my $log, "<", "mktest.out" or return;
	while (<$log>) {
	    chomp;
	    s/\s+$//;
	    if (s/^Configuration:\s*//) {
		s/\s+$//;
		s/-Dusedevel\s*//;
		s/^\Q$log{$host}{$cc}{cnf}\E\s*//;
		$dbg = s/\s*(-DDEBUGGING)\b// ? $1 : "";
		$conf = $_;
		$rpt{conf}{$conf}{$dbg}++;
		next;
		}
	    s/^\s*TSTENV\s+=\s*(?:locale:)?(\S+)\s*// and
		$env = $1;
	    my ($ntests, $cpu);
	    if (m/\bFiles=[0-9]+,\s*Tests=([0-9]+),.*?=\s*([0-9.]+)\s*CPU/) {
		($ntests, $cpu) = ($1, $2);
		}
	    elsif (m/\b u=([0-9.]+)\s+
			s=([0-9.]+)\s+
			cu=([0-9.]+)\s+
			cs=([0-9.]+)\s+
			scripts=[0-9]+\s+
			tests=([0-9]+)/xi) {
		($ntests, $cpu) = ($5, $1 + $2 + $3 + $4);
		}
	    if ($ntests) {
		my $speed = 25_000 * $cpu / $ntests;
		$speed < $rpt{speed}{min} and $rpt{speed}{min} = $speed;
		$speed > $rpt{speed}{max} and $rpt{speed}{max} = $speed;
		$log{$host}{$cc}{speed}{$conf}{$dbg}{$env} = $speed;
		$ntests = 0;
		push @{$rpt{speed}{$cc}{$conf}{$dbg}{$env}}, $speed;
		}
	    }
	}, glob "*/511/*/mktest.rpt");

    #DDumper $log{tux}, \%rpt;
    print $hh qq{<h2>Speed report</h2>\n\n},
	      qq{<div class="speed">\n},
	      qq{  <table border="1">\n},
	      qq{    <tr>\n},
	      qq{      <td>&nbsp;</td>\n};
    print $hh qq{      <td>$_->[1]</td>\n} for @host;
    print $hh qq{      </tr>\n};

    foreach my $conf (map  { $_->[0] }
		      sort { $a->[1] <=> $b->[1] ||
			     $a->[2] cmp $b->[2] }
		      map  { m/-Duseithreads\s*(.*)/ ? [ $_, 1, $1 ] : [ $_, 0, $_ ] }
		      keys %{$rpt{conf}}) {
	foreach my $dbg ("", "-DDEBUGGING") {
	    exists $rpt{conf}{$conf}{$dbg} or next;
	    my $class = $dbg ? "debug" : "normal";
	    print $hh qq{    <tr class="$class">\n      <td class="conf">$conf</td>\n};

	    foreach my $host (@host) {
		my $speed = join " / ", map {
		    my $x = $log{$host->[0]}{$_}{speed}{$conf}{$dbg}{perlio};
		    $x ? sprintf "%.1f", $x : "" } "cc", "gcc";
		print $hh qq{      <td class="center">$speed</td>\n};
		}
	    print $hh qq{      </tr>\n};
	    }
	}

    print $hh qq{    <tr>\n},
	      qq{      <td><span class="normal">NO DEBUGGING</span> / <span class="debug">-DDEBUGGING</span></td>\n},
	      qq{      <td class="center">cc&nbsp; / gcc</td>\n},
	      qq{      <td colspan="8">Lower values are better (faster)</td>\n},
	      qq{      </tr>\n},
	      qq{    </table>\n},
	      qq{  </div>\n\n},
	      qq{<table>\n};

    # Compare major diffs ...
    # Be fair! Only compare non-XXX to XXX if both exist for the same host/cc

    my (@n, @x);

    my $diff = sub {
	my $s = shift;
	my $z = shift // "non-$s";

	unless (@n && @x) {
	    # warn "$s: not enough data\n";
	    @n = @x = ();
	    return;
	    }

	my $n = scalar @n;
	my $x = scalar @x;

	my $N = sum (@n) / $n;
	my $X = sum (@x) / $x;

	my $r = $N / ($X - $N);
	my $R = $r < 0 ? "faster" : "slower";
	my $sr = sprintf "%.1f%%", abs $r;

	if ($opt_v) {
	    printf STDERR "%6.1f (%4d) - %6.1f (%4d): %-12s is %7s% %s than %s\n",
		$N, $n, $X, $x, $s, $sr, $R, $z;
	    }
	    #printf STDERR "%-12s is %7s %s than %s\n", $s, $sr, $R, $z;
	print $hh qq{  <tr><td>$s</td><td>is</td><td class="right">$sr</td><td>$R</td><td>than $z</td></tr>\n};
	@n = @x = ();
	}; # diff

    my $push = sub {
	my ($n, $x) = @_;
	ref $n eq "ARRAY" or return;
	ref $x eq "ARRAY" or return;
	my @N = @$n;
	my @X = @$x;
	@N == @X && sum (@N) && sum (@X) or return;
	push @n, @N;
	push @x, @X;
	}; # push

    # 1. Threaded vs non-treaded
    {   foreach my $cc ("cc", "gcc") {
	    foreach my $conf (keys %{$rpt{speed}{$cc}}) {
		$conf =~ m/-Dusei?threads/ or next;
		foreach my $dbg (keys %{$rpt{speed}{$cc}{$conf}}) {
		    foreach my $env (keys %{$rpt{speed}{$cc}{$conf}{$dbg}}) {
			(my $c = $conf) =~ s/-Dusei?threads\s*//;
			$push->($rpt{speed}{$cc}{$c}{$dbg}{$env},
				$rpt{speed}{$cc}{$conf}{$dbg}{$env});
			}
		    }
		}
	    }
	$diff->("threaded");
	}

    # 2. Debugging vs non-debugging
    {   foreach my $cc ("cc", "gcc") {
	    foreach my $conf (keys %{$rpt{speed}{$cc}}) {
		foreach my $dbg (keys %{$rpt{speed}{$cc}{$conf}}) {
		    $dbg or next;
		    foreach my $env (keys %{$rpt{speed}{$cc}{$conf}{$dbg}}) {
			$push->($rpt{speed}{$cc}{$conf}{""}{$env},
				$rpt{speed}{$cc}{$conf}{$dbg}{$env});
			}
		    }
		}
	    }
	$diff->("DEBUGGING");
	}

    # 3. native cc vs gcc/g++
    {	foreach my $conf (keys %{$rpt{speed}{gcc}}) {
	    foreach my $dbg (keys %{$rpt{speed}{gcc}{$conf}}) {
		foreach my $env (keys %{$rpt{speed}{gcc}{$conf}{$dbg}}) {
		    $push->($rpt{speed}{cc}{$conf}{$dbg}{$env},
			    $rpt{speed}{gcc}{$conf}{$dbg}{$env});
		    }
		}
	    }
	$diff->("gcc/g++", "cc");
	}

    # 4. Debugging vs non-debugging
    {   foreach my $cc ("cc", "gcc") {
	    foreach my $conf (keys %{$rpt{speed}{$cc}}) {
		foreach my $dbg (keys %{$rpt{speed}{$cc}{$conf}}) {
		    $push->($rpt{speed}{$cc}{$conf}{$dbg}{perlio},
			    $rpt{speed}{$cc}{$conf}{$dbg}{stdio});
		    }
		}
	    }
	$diff->("stdio", "perlio");
	}

    print $hh qq{  </table>\n};
    } # speed_report
