aboutsummaryrefslogtreecommitdiff
path: root/test-suite/compare_lnt_benchmarks.pl
diff options
context:
space:
mode:
Diffstat (limited to 'test-suite/compare_lnt_benchmarks.pl')
-rwxr-xr-xtest-suite/compare_lnt_benchmarks.pl304
1 files changed, 304 insertions, 0 deletions
diff --git a/test-suite/compare_lnt_benchmarks.pl b/test-suite/compare_lnt_benchmarks.pl
new file mode 100755
index 0000000..f0d5139
--- /dev/null
+++ b/test-suite/compare_lnt_benchmarks.pl
@@ -0,0 +1,304 @@
+#!/usr/bin/env perl
+# This program handles the results of the LLVM test-suite benchmark.
+# There are two stages:
+# prepare: collates multiple samples into a single log file containing
+# the arithmetic mean of each tests' results.
+# compare: compares two collated results and produce a file containing
+# the relative values between baseline and target runs, with
+# the geomean of compile and execution time at the end.
+#
+# Suggested usage:
+#
+# ... point install directory to the unmodified / previous compiler
+# $ ./run.sh -b
+# $ ./compare_lnt_benchmarks.pl prepare sanbox/build > baseline.txt
+# ... change install directory to the modified / current compiler
+# $ ./run.sh -b
+# $ ./compare_lnt_benchmarks.pl prepare sanbox/build > target.txt
+# $ ./compare_lnt_benchmarks.pl compare baseline.txt target.txt > results.txt
+
+use strict;
+use warnings;
+use Scalar::Util qw(looks_like_number);
+use Statistics::Descriptive;
+use Data::Dumper;
+
+my $syntax = "Syntax: $0 <action> [action options]\n".
+ "Actions:\n".
+ "\tprepare: reads all report files in sample-N, collate into one run\n".
+ "\t options: <sanbox/build> with report.txt or sample-N\n".
+ "\tcompare: compares two runs, taking geomean of all benchmarks\n".
+ "\t options: <baseline.report.txt> <target.report.txt>\n";
+my $action = $ARGV[0];
+die $syntax unless defined $action;
+
+###################################################
+
+if ($action eq "prepare") {
+ die $syntax unless defined $ARGV[1];
+ &prepare($ARGV[1]);
+} elsif ($action eq "compare") {
+ die $syntax unless defined $ARGV[1] and defined $ARGV[2];
+ &compare($ARGV[1], $ARGV[2]);
+} else {
+ die $syntax;
+}
+exit;
+
+###################################################
+
+# prepare a number of logs into a collated results by average
+sub prepare($) {
+ my ($basedir) = @_;
+ my $logname = "report.simple.txt";
+
+ # Single report, just copy
+ if (-f "$basedir/$logname") {
+ my %results;
+ my $header = &read_file("$basedir/$logname", \%results);
+ &dump(\%results, $header);
+
+ # Multiple reports, collate
+ } elsif (-f "$basedir/sample-0/$logname") {
+ my @results;
+ my $header;
+
+ # List all sample files
+ opendir DIR, $basedir || die "Can't open '$basedir': $!\n";
+ my @samples = grep { /sample-\d+/ && -f "$basedir/$_/$logname" } readdir DIR;
+ closedir DIR;
+ die "Basedir '$basedir' has no txt logs\n" unless scalar @samples;
+
+ # For each sample, read&push
+ foreach my $s (@samples) {
+ my %sample;
+ $header = &read_file("$basedir/$s/$logname", \%sample);
+ push @results, \%sample;
+ }
+
+ # Collate results
+ my %result;
+ &collate_results(\@results, \%result);
+ &dump(\%result, $header);
+
+ } else {
+ die "Basedir '$basedir' has no txt logs\n"
+ }
+}
+
+# compare two logs, producing the relative results and geomean
+sub compare($$) {
+ my ($baseline_file, $target_file) = @_;
+ die "Baseline report file '$baseline_file' doesn't exist or is not a file\n"
+ unless -f $baseline_file;
+ die "Target report file '$target_file' doesn't exist or is not a file\n"
+ unless -f $target_file;
+ my (%baseline, %target, %result) = ((), (), ());
+
+ my $header = &read_file($baseline_file, \%baseline);
+ &read_file($target_file, \%target);
+
+ &compare_results(\%baseline, \%target, \%result);
+
+ &dump(\%result, '', 1);
+}
+
+###################################################
+
+# reads a file, saving all data indexed by prog name
+# returns the header for further use
+sub read_file($$) {
+ my ($filename, $result) = @_;
+ my $header = '';
+ my $max = 1;
+ open FH, $filename || die "Can't open $filename: $!\n";
+ while (<FH>) {
+ chomp();
+ my ($program, $sep,
+ $cc_pass, $cc_time, $cc_real,
+ $ex_pass, $ex_time, $ex_real) = split /\s+/;
+ # Make sure we have the right file
+ if (!$header) {
+ die "Invalid header in $filename\n"
+ unless $program eq "Program"
+ and $cc_time eq "CC_Time"
+ and $ex_time eq "Exec_Time";
+ $header = $_;
+ next;
+ }
+ # Log each non-header line
+ $result->{$program} = {
+ 'cc_pass' => $cc_pass,
+ 'cc_time' => $cc_time,
+ 'cc_real' => $cc_real,
+ 'ex_pass' => $ex_pass,
+ 'ex_time' => $ex_time,
+ 'ex_real' => $ex_real,
+ };
+
+ # Max length prog name
+ my $len = length($program);
+ $max = $len if $len > $max;
+ }
+ close FH;
+
+ $result->{'max'} = $max;
+
+ return $header;
+}
+
+# returns "pass" AND "pass"
+sub pass_diff($$) {
+ my ($baseline, $target) = @_;
+ if (defined $baseline and defined $target and
+ $baseline eq "pass" and $target eq "pass") {
+ return "pass";
+ } elsif (!$baseline and $target) {
+ return $target;
+ } elsif ($baseline and !$target) {
+ return $baseline;
+ } else {
+ return "fail";
+ }
+}
+
+# collate multiple reports into one, by getting the
+# average of each result into each field
+sub collate_results($$) {
+ my ($results, $result) = @_;
+
+ foreach my $sample (@$results) {
+ foreach my $prog (keys %$sample) {
+ next unless ref $sample->{$prog} eq "HASH";
+
+ # Set up statistics
+ if (!defined $result->{$prog}) {
+ $result->{$prog}->{'cc_time'} = new Statistics::Descriptive::Full();
+ $result->{$prog}->{'cc_real'} = new Statistics::Descriptive::Full();
+ $result->{$prog}->{'ex_time'} = new Statistics::Descriptive::Full();
+ $result->{$prog}->{'ex_real'} = new Statistics::Descriptive::Full();
+ }
+ # gather results
+ $result->{$prog}->{'cc_time'}->add_data($sample->{$prog}->{'cc_time'});
+ $result->{$prog}->{'cc_real'}->add_data($sample->{$prog}->{'cc_real'});
+ $result->{$prog}->{'ex_time'}->add_data($sample->{$prog}->{'ex_time'});
+ $result->{$prog}->{'ex_real'}->add_data($sample->{$prog}->{'ex_real'});
+ # collate pass status
+ $result->{$prog}->{'cc_pass'} =
+ &pass_diff($result->{$prog}->{'cc_pass'},
+ $sample->{$prog}->{'cc_pass'});
+ $result->{$prog}->{'ex_pass'} =
+ &pass_diff($result->{$prog}->{'ex_pass'},
+ $sample->{$prog}->{'ex_pass'});
+ }
+ # All max should be the same
+ $result->{'max'} = $sample->{'max'};
+ }
+ # collate results
+ foreach my $prog (keys %$result) {
+ next unless ref $result->{$prog} eq "HASH";
+ $result->{$prog}->{'cc_time'} = $result->{$prog}->{'cc_time'}->mean();
+ $result->{$prog}->{'cc_real'} = $result->{$prog}->{'cc_real'}->mean();
+ $result->{$prog}->{'ex_time'} = $result->{$prog}->{'ex_time'}->mean();
+ $result->{$prog}->{'ex_real'} = $result->{$prog}->{'ex_real'}->mean();
+ }
+}
+
+# compares two aggregated logs with absolute values
+# and creates a final result with relative values
+sub compare_results($$$) {
+ my ($baseline, $target, $result) = @_;
+ my $cc_geo = new Statistics::Descriptive::Full();
+ my $ex_geo = new Statistics::Descriptive::Full();
+
+ $result->{'cc_pass'} = "pass";
+ $result->{'ex_pass'} = "pass";
+ foreach my $prog (keys %$baseline) {
+ next unless ref $baseline->{$prog} eq "HASH";
+ die "Program '$prog' in baseline doesn't exist in target\n"
+ unless defined $target->{$prog};
+
+ # No zero on divisions
+ my ($b, $t) = ($baseline->{$prog}, $target->{$prog});
+ foreach my $k (keys %$b) {
+ $b->{$k} = 0.001 if looks_like_number($b->{$k})
+ and $b->{$k} == 0.0;
+ }
+ foreach my $k (keys %$t) {
+ $t->{$k} = 0.001 if looks_like_number($t->{$k})
+ and $t->{$k} == 0.0;
+ }
+
+ # Proportional difference
+ $result->{$prog} = {
+ 'cc_pass' => &pass_diff($b->{'cc_pass'}, $t->{'cc_pass'}),
+ 'cc_time' => ($t->{'cc_time'} / $b->{'cc_time'}) * 100,
+ 'cc_real' => ($t->{'cc_real'} / $b->{'cc_real'}) * 100,
+ 'ex_pass' => &pass_diff($b->{'ex_pass'}, $t->{'ex_pass'}),
+ 'ex_time' => ($t->{'ex_time'} / $b->{'ex_time'}) * 100,
+ 'ex_real' => ($t->{'ex_real'} / $b->{'ex_real'}) * 100,
+ };
+
+ # Add data to statistical model (ignore real)
+ $cc_geo->add_data($result->{$prog}->{'cc_time'});
+ $ex_geo->add_data($result->{$prog}->{'ex_time'});
+
+ # Update global "pass" status
+ $result->{'cc_pass'} = &pass_diff($result->{'cc_pass'},
+ $result->{$prog}->{'cc_pass'});
+ $result->{'ex_pass'} = &pass_diff($result->{'ex_pass'},
+ $result->{$prog}->{'ex_pass'});
+ }
+
+ # Get geomean of all differences / max length of prog name
+ $result->{'cc_geo'} = $cc_geo->geometric_mean();
+ $result->{'ex_geo'} = $ex_geo->geometric_mean();
+ $result->{'max'} = $baseline->{'max'};
+}
+
+# dumps the file in the same format as read
+# can dump the header or geomean, if requested
+sub dump($$) {
+ my ($result, $header, $geo) = @_;
+ my $max = $result->{'max'};
+
+ # If we have the header, print it
+ if ($header) {
+ print $header."\n";
+ }
+
+ foreach my $prog (sort keys %$result) {
+ next unless ref $result->{$prog} eq "HASH";
+
+ # Dump the program name, with spaces at the end
+ my $p = $result->{$prog};
+ my $spaces = $max - length($prog);
+ print $prog;
+ print ' ' x $spaces;
+ print "\t|\t";
+
+ # Compile time
+ print "\t".$p->{'cc_pass'};
+ printf("\t%0.4f", $p->{'cc_time'});
+ printf("\t%0.4f", $p->{'cc_real'});
+
+ # Execution time
+ printf "\t".$p->{'ex_pass'};
+ printf("\t%0.4f", $p->{'ex_time'});
+ printf("\t%0.4f", $p->{'ex_real'});
+
+ print "\n";
+ }
+
+ # Print the final geomean line
+ if ($geo) {
+ print "GEOMEAN";
+ print ' ' x ($max - 7);
+ print "\t|\t";
+ print "\t".$result->{'cc_pass'};
+ printf("\t%0.2f\t", $result->{'cc_geo'});
+ print "\t".$result->{'ex_pass'};
+ printf("\t%0.2f", $result->{'ex_geo'});
+ print "\n";
+ }
+}