#!/usr/bin/perl # # parse-localbenchmarks - convert localbenchmarks output to CSV # # This is a filter. It transforms data from one format to another. Usage: # # $ make localbenchmarks &> mylogfile # $ hack/parse-localbenchmarks benchmarks.csv # # To be more precise, this is a very stupid simpleminded filter. It is # not a complete solution to the benchmarks problem. In particular, # other tools are still needed to: # # * Actually _run_ the benchmarks in some standard production environment # * Run this script on the results # * Save results, with identifying tags (datetime, git hash, PR id, ...) # * Compare two or more sets of CSVs # (our $ME = $0) =~ s|^.*/||; # script name use v5.14; use utf8; # FIXME: add --help. Some day. Not urgent. die "$ME: This is a filter, not an interactive tool\n" if -t *STDIN; my $n_samples; # Number of timing runs (FIXME: unused) my %results; # Timing results my @benchmarks; # Names of benchmarks my ($type, $testname); # Current context # # Pass 1: read in timings # while (my $line = ) { # Log will have lots of ginkgo output. The only thing we care about is # the summary at the end, which will look something like: # # * [MEASUREMENT] # Podman Benchmark Suite # .... # Ran 3 samples: # [CPU] podman images: # Fastest Time: 0.265s # Slowest Time: 0.322s # Average Time: 0.302s ± 0.018s # [MEM] podman images: # Smallest: 44076.0KB # Largest: 44616.0KB # Average: 44338.7KB ± 171.2KB # [CPU] podman push: # ....repeat [CPU] and [MEM] for each test # -------------------------- # SSSSSSSSSSSSSSSSSSSSS (and more ginkgo output we don't care about) # chomp $line; next unless $line =~ /^.{1,3}\s+\[MEASUREMENT\]/ .. $line =~ /^-{20,}$/; # Trim leading & trailing whitespace $line =~ s/(^\s+|\s+$)//g; # FIXME: we don't actually emit this. What would be a good way to do so? if ($line =~ /^Ran\s+(\d+)\s+samples/) { $n_samples = $1; } # e.g., [CPU] podman foo: elsif ($line =~ /^\[([A-Z]+)\]\s+(\S.*\S):$/) { ($type, $testname) = ($1, $2); } # e.g., 'Fastest Time: 0.265s' elsif ($line =~ /^(\S.*?\S):\s+(.*)/) { log_result($testname, $type, $1, $2); } else { warn "Cannot grok '$line'\n" if $ENV{DEBUG_PARSELOCALBENCHMARKS}; } } # # Pass 2: write out CSV # # Headings... print "\"Test Name\""; printf ", \"%s\"", $_ for @benchmarks; print "\n"; # ...then data for my $t (sort keys %results) { printf "\"%s\"", $t; for my $benchmark (@benchmarks) { printf ", \"%s\"", $results{$t}{$benchmark} || ''; } print "\n"; } exit 0; ################ # log_result # Preserve one record ################ sub log_result { my $testname = shift; # in: test name (eg "podman foo") my $type = shift; # in: CPU or MEM my $name = shift; # in: benchmark name (eg "Fastest") my $result = shift; # in: benchmark value my $benchmark = "$type $name"; $results{$testname}{$benchmark} = $result; # Keep an ordered list of benchmark names (as in, the order we # encounter them) push @benchmarks, $benchmark unless grep { $_ eq $benchmark } @benchmarks; # Special case: "Average X" may be of the form "xxx ± yyy". Move the # standard deviation to its own column. if ($name =~ /Average/) { if ($results{$testname}{$benchmark} =~ s/^(\S+)\s+.*\s+(\S+)$/$1/) { my ($average, $sd) = ($1, $2); log_result($testname, $type, 'StdDev', $sd); # Strip off units, so we can determine it as a percentage $average =~ s/[a-z]+$//i; $sd =~ s/[a-z]+$//i; my $pct = sprintf("%.1f%%", $sd * 100.0 / $average); log_result($testname, $type, 'StdDev (Percent)', $pct); } } }