summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorTvrtko Ursulin <tvrtko.ursulin@intel.com>2020-06-17 16:04:17 +0100
committerTvrtko Ursulin <tvrtko.ursulin@intel.com>2020-06-19 16:37:11 +0100
commiteba9936b06e1dab092a965a20df12ea61c00ef66 (patch)
treee10e1777e4050a4840f903af678c3f95fb89aa89 /scripts
parenteba1135ddd35f9d3097ed91032aefe8f9a9f9d02 (diff)
gem_wsim: Rip out userspace balancing
Evaluation of userspace load balancing options was how this tool started but since we have settled on doing it in the kernel. Tomorrow we will want to update the tool for new engine interfaces and all this legacy code will just be a distraction. Rip out everything not related to explicit load balancing implemented via context engine maps and adjust the workloads to use it. Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> Acked-by: Chris Wilson <chris@chris-wilson.co.uk>
Diffstat (limited to 'scripts')
-rw-r--r--scripts/Makefile.am2
-rwxr-xr-xscripts/media-bench.pl736
2 files changed, 1 insertions, 737 deletions
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index e26a39e2..64171529 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -1,2 +1,2 @@
-dist_noinst_SCRIPTS = intel-gfx-trybot who.sh run-tests.sh trace.pl media-bench.pl
+dist_noinst_SCRIPTS = intel-gfx-trybot who.sh run-tests.sh trace.pl
noinst_PYTHON = throttle.py
diff --git a/scripts/media-bench.pl b/scripts/media-bench.pl
deleted file mode 100755
index 1cd8205f..00000000
--- a/scripts/media-bench.pl
+++ /dev/null
@@ -1,736 +0,0 @@
-#! /usr/bin/perl
-#
-# Copyright © 2017 Intel Corporation
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice (including the next
-# paragraph) shall be included in all copies or substantial portions of the
-# Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-# IN THE SOFTWARE.
-#
-
-use strict;
-use warnings;
-use 5.010;
-
-use Getopt::Std;
-
-chomp(my $igt_root = `pwd -P`);
-my $wsim = "$igt_root/benchmarks/gem_wsim";
-my $wrk_root = "$igt_root/benchmarks/wsim";
-my $tracepl = "$igt_root/scripts/trace.pl";
-my $tolerance = 0.01;
-my $client_target_s = 10;
-my $idle_tolerance_pct = 2.0;
-my $verbose = 0;
-my $gt2 = 0;
-my $show_cmds = 0;
-my $realtime_target = 0;
-my $wps_target = 0;
-my $wps_target_param = 0;
-my $multi_mode = 0;
-my @multi_workloads;
-my $w_direct;
-my $balancer;
-my $nop;
-my %opts;
-
-my @balancers = ( 'rr', 'rand', 'qd', 'qdr', 'qdavg', 'rt', 'rtr', 'rtavg',
- 'context', 'busy', 'busy-avg', 'i915' );
-my %bal_skip_H = ( 'rr' => 1, 'rand' => 1, 'context' => 1, , 'busy' => 1,
- 'busy-avg' => 1, 'i915' => 1 );
-my %bal_skip_R = ( 'i915' => 1 );
-my %bal_skip_G = ( 'i915' => 1 );
-
-my @workloads = (
- 'media_load_balance_17i7.wsim',
- 'media_load_balance_19.wsim',
- 'media_load_balance_4k12u7.wsim',
- 'media_load_balance_fhd26u7.wsim',
- 'media_load_balance_hd01.wsim',
- 'media_load_balance_hd06mp2.wsim',
- 'media_load_balance_hd12.wsim',
- 'media_load_balance_hd17i4.wsim',
- 'media_1n2_480p.wsim',
- 'media_1n3_480p.wsim',
- 'media_1n4_480p.wsim',
- 'media_1n5_480p.wsim',
- 'media_1n2_asy.wsim',
- 'media_1n3_asy.wsim',
- 'media_1n4_asy.wsim',
- 'media_1n5_asy.wsim',
- 'media_mfe2_480p.wsim',
- 'media_mfe3_480p.wsim',
- 'media_mfe4_480p.wsim',
- 'media_nn_1080p.wsim',
- 'media_nn_480p.wsim',
- );
-
-sub show_cmd
-{
- my ($cmd) = @_;
-
- say "\n+++ $cmd" if $show_cmds;
-}
-
-sub calibrate_nop
-{
- my ($delay, $nop);
- my $cmd = "$wsim";
-
- show_cmd($cmd);
- open WSIM, "$cmd |" or die;
- while (<WSIM>) {
- chomp;
- if (/Nop calibration for (\d+)us delay is (\d+)./) {
- $delay = $1;
- $nop = $2;
- }
-
- }
- close WSIM;
-
- die unless $nop;
-
- return $nop
-}
-
-sub can_balance_workload
-{
- my ($wrk) = @_;
- my $res = 0;
-
- open WRK, "$wrk_root/$wrk" or die;
- while (<WRK>) {
- chomp;
- if (/\.VCS\./) {
- $res = 1;
- last;
- }
- }
- close WRK;
-
- return $res;
-}
-
-sub add_wps_arg
-{
- my (@args) = @_;
- my $period;
-
- return @args if $realtime_target <= 0;
-
- $period = int(1000000 / $realtime_target);
- push @args, '-a';
- push @args, 'p.$period';
-
- return @args;
-}
-
-sub run_workload
-{
- my (@args) = @_;
- my ($time, $wps, $cmd);
- my @ret;
-
- @args = add_wps_arg(@args);
- push @args, '-2' if $gt2;
-
- unshift @args, $wsim;
- $cmd = join ' ', @args;
- show_cmd($cmd);
-
- open WSIM, "$cmd |" or die;
- while (<WSIM>) {
- chomp;
- if (/^(\d+\.\d+)s elapsed \((\d+\.?\d+) workloads\/s\)$/) {
- $time = $1;
- $wps = $2;
- } elsif (/(\d+)\: \d+\.\d+s elapsed \(\d+ cycles, (\d+\.?\d+) workloads\/s\)/) {
- $ret[$1] = $2;
- }
- }
- close WSIM;
-
- return ($time, $wps, \@ret);
-}
-
-sub dump_cmd
-{
- my ($cmd, $file) = @_;
-
- show_cmd("$cmd > $file");
-
- open FOUT, '>', $file or die;
- open TIN, "$cmd |" or die;
- while (<TIN>) {
- print FOUT $_;
- }
- close TIN;
- close FOUT;
-}
-
-sub trace_workload
-{
- my ($wrk, $b, $r, $c) = @_;
- my @args = ($tracepl, '--trace', $wsim, '-q', '-n', $nop, '-r', $r, '-c', $c);
- my $min_batches = 16 + $r * $c / 2;
- my @skip_engine;
- my %engines;
- my ($cmd, $file);
-
- push @args, '-2' if $gt2;
-
- unless ($b eq '<none>') {
- push @args, '-R';
- push @args, split /\s+/, $b;
- }
-
- if (defined $w_direct) {
- push @args, split /\s+/, $wrk;
- } else {
- push @args, '-w';
- push @args, $wrk_root . '/' . $wrk;
- }
-
- show_cmd(join ' ', @args);
- if (-e 'perf.data') {
- unlink 'perf.data' or die;
- }
- system(@args) == 0 or die;
-
- $cmd = "perf script | $tracepl";
- show_cmd($cmd);
- open CMD, "$cmd |" or die;
- while (<CMD>) {
- chomp;
- if (/Ring(\S+): (\d+) batches.*?(\d+\.?\d+)% idle,/) {
- if ($2 >= $min_batches) {
- $engines{$1} = $3;
- } else {
- push @skip_engine, $1;
- }
- } elsif (/GPU: (\d+\.?\d+)% idle/) {
- $engines{'gpu'} = $1;
- }
- }
- close CMD;
-
- $wrk =~ s/$wrk_root//g;
- $wrk =~ s/\.wsim//g;
- $wrk =~ s/-w/W/g;
- $wrk =~ s/[ -]/_/g;
- $wrk =~ s/\//-/g;
- $b =~ s/[ <>]/_/g;
- $file = "${wrk}_${b}_-r${r}_-c${c}";
-
- dump_cmd('perf script', "${file}.trace");
-
- $cmd = "perf script | $tracepl --html -x ctxsave -s -c ";
- $cmd .= join ' ', map("-i $_", @skip_engine);
-
- dump_cmd($cmd, "${file}.html");
-
- return \%engines;
-}
-
-sub calibrate_workload
-{
- my ($wrk) = @_;
- my $tol = $tolerance;
- my $loops = 0;
- my $error;
- my $r;
-
- $r = $realtime_target > 0 ? $realtime_target * $client_target_s : 23;
- for (;;) {
- my @args = ('-n', $nop, '-r', $r);
- my ($time, $wps);
-
- if (defined $w_direct) {
- push @args, split /\s+/, $wrk;
- } else {
- push @args, '-w';
- push @args, $wrk_root . '/' . $wrk;
- }
-
- ($time, $wps) = run_workload(@args);
-
- $wps = $r / $time if $w_direct;
- $error = abs($time - $client_target_s) / $client_target_s;
-
- last if $error <= $tol;
-
- $r = int($wps * $client_target_s);
- $loops = $loops + 1;
- if ($loops >= 3) {
- $tol = $tol * (1.2 + ($tol));
- $loops = 0;
- }
- last if $tol > 0.2;
- }
-
- return ($r, $error);
-}
-
-sub find_saturation_point
-{
- my ($wrk, $rr, $verbose, @args) = @_;
- my ($last_wps, $c, $swps, $wwps);
- my $target = $realtime_target > 0 ? $realtime_target : $wps_target;
- my $r = $rr;
- my $wcnt;
- my $maxc;
- my $max = 0;
-
- push @args, '-v' if $multi_mode and $w_direct;
-
- if (defined $w_direct) {
- push @args, split /\s+/, $wrk;
- $wcnt = () = $wrk =~ /-[wW]/gi;
-
- } else {
- push @args, '-w';
- push @args, $wrk_root . '/' . $wrk;
- $wcnt = 1;
- }
-
- for ($c = 1; ; $c = $c + 1) {
- my ($time, $wps);
- my @args_ = (@args, ('-r', $r, '-c', $c));
-
- ($time, $wps, $wwps) = run_workload(@args_);
-
- say " $c clients is $wps wps." if $verbose;
-
- if ($c > 1) {
- my $delta;
-
- if ($target <= 0) {
- if ($wps > $max) {
- $max = $wps;
- $maxc = $c;
- }
- $delta = ($wps - $last_wps) / $last_wps;
- if ($delta > 0) {
- last if $delta < $tolerance;
- } else {
- $delta = ($wps - $max) / $max;
- last if abs($delta) >= $tolerance;
- }
- } else {
- $delta = ($wps / $c - $target) / $target;
- last if $delta < 0 and abs($delta) >= $tolerance;
- }
- $r = int($rr * ($client_target_s / $time));
- } elsif ($c == 1) {
- $swps = $wps;
- return ($c, $wps, $swps, $wwps) if $wcnt > 1 or
- $multi_mode or
- ($wps_target_param < 0 and
- $wps_target == 0);
- }
-
- $last_wps = $wps;
- }
-
- if ($target <= 0) {
- return ($maxc, $max, $swps, $wwps);
- } else {
- return ($c - 1, $last_wps, $swps, $wwps);
- }
-}
-
-getopts('hv2xmn:b:W:B:r:t:i:R:T:w:', \%opts);
-
-if (defined $opts{'h'}) {
- print <<ENDHELP;
-Supported options:
-
- -h Help text.
- -v Be verbose.
- -x Show external commands.
- -2 Run gem_wsim in GT2 mode.
- -n num Nop calibration.
- -b str Balancer to pre-select.
- Skips balancer auto-selection.
- Passed straight the gem_wsim so use like -b "-b qd -R"
- -W a,b,c Override the default list of workloads.
- -B a,b,c Override the default list of balancers.
- -r sec Target workload duration.
- -t pct Calibration tolerance.
- -i pct Engine idleness tolerance.
- -R wps Run workloads in the real-time mode at wps rate.
- -T wps Calibrate up to wps/client target instead of GPU saturation.
- Negative values set the target based on the single client
- performance where target = single-client-wps / -N.
- -w str Pass-through to gem_wsim. Overrides normal workload selection.
- -m Multi-workload mode. All selected workloads will be run in
- parallel and overal score will be relative to when run
- individually.
-ENDHELP
- exit 0;
-}
-
-$verbose = 1 if defined $opts{'v'};
-$gt2 = 1 if defined $opts{'2'};
-$show_cmds = 1 if defined $opts{'x'};
-$multi_mode = 1 if defined $opts{'m'};
-if (defined $opts{'b'}) {
- die unless substr($opts{'b'}, 0, 2) eq '-b';
- $balancer = $opts{'b'};
-}
-if (defined $opts{'B'}) {
- @balancers = split /,/, $opts{'B'};
-} else {
- unshift @balancers, '';
-}
-@workloads = split /,/, $opts{'W'} if defined $opts{'W'};
-$client_target_s = $opts{'r'} if defined $opts{'r'};
-$tolerance = $opts{'t'} / 100.0 if defined $opts{'t'};
-$idle_tolerance_pct = $opts{'i'} if defined $opts{'i'};
-$realtime_target = $opts{'R'} if defined $opts{'R'};
-$wps_target = $opts{'T'} if defined $opts{'T'};
-$wps_target_param = $wps_target;
-$w_direct = $opts{'w'} if defined $opts{'w'};
-
-if ($multi_mode) {
- die if $w_direct; # Not supported
- @multi_workloads = @workloads;
-}
-
-@workloads = ($w_direct) if defined $w_direct;
-
-say "Workloads:";
-print map { " $_\n" } @workloads;
-print "Balancers: ";
-say map { "$_," } @balancers;
-say "Target workload duration is ${client_target_s}s.";
-say "Calibration tolerance is $tolerance.";
-say "Real-time mode at ${realtime_target} wps." if $realtime_target > 0;
-say "Wps target is ${wps_target} wps." if $wps_target > 0;
-say "Multi-workload mode." if $multi_mode;
-$nop = $opts{'n'};
-$nop = calibrate_nop() unless $nop;
-say "Nop calibration is $nop.";
-
-goto VERIFY if defined $balancer;
-
-my (%best_bal, %best_bid);
-my %results;
-my %scores;
-my %wscores;
-my %cscores;
-my %cwscores;
-my %mscores;
-my %mwscores;
-
-sub add_points
-{
- my ($wps, $scores, $wscores) = @_;
- my ($min, $max, $spread);
- my @sorted;
-
- @sorted = sort { $b <=> $a } values %{$wps};
- $max = $sorted[0];
- $min = $sorted[-1];
- $spread = $max - $min;
- die if $spread < 0;
-
- foreach my $w (keys %{$wps}) {
- my ($score, $wscore);
-
- unless (exists $scores->{$w}) {
- $scores->{$w} = 0;
- $wscores->{$w} = 0;
- }
-
- $score = $wps->{$w} / $max;
- $scores->{$w} = $scores->{$w} + $score;
- $wscore = $score * $spread / $max;
- $wscores->{$w} = $wscores->{$w} + $wscore;
- }
-}
-
-my @saturation_workloads = $multi_mode ? @multi_workloads : @workloads;
-my %allwps;
-my $widx = 0;
-
-push @saturation_workloads, '-w ' . join ' -w ', map("$wrk_root/$_", @workloads)
- if $multi_mode;
-
-foreach my $wrk (@saturation_workloads) {
- my @args = ( "-n $nop");
- my ($r, $error, $should_b, $best);
- my (%wps, %cwps, %mwps);
- my @sorted;
- my $range;
-
- $w_direct = $wrk if $multi_mode and $widx == $#saturation_workloads;
-
- $should_b = 1;
- $should_b = can_balance_workload($wrk) unless defined $w_direct;
-
- print "\nEvaluating '$wrk'...";
-
- ($r, $error) = calibrate_workload($wrk);
- say " ${client_target_s}s is $r workloads. (error=$error)";
-
- say " Finding saturation points for '$wrk'...";
-
- BAL: foreach my $bal (@balancers) {
- GBAL: foreach my $G ('', '-G', '-d', '-G -d') {
- foreach my $H ('', '-H') {
- my @xargs;
- my ($w, $c, $s, $bwwps);
- my $bid;
-
- if ($bal ne '') {
- next GBAL if $G =~ '-G' and exists $bal_skip_G{$bal};
-
- push @xargs, "-b $bal";
- push @xargs, '-R' unless exists $bal_skip_R{$bal};
- push @xargs, $G if $G ne '';
- push @xargs, $H if $H ne '';
- $bid = join ' ', @xargs;
- print " $bal balancer ('$bid'): ";
- } else {
- $bid = '<none>';
- print " No balancing: ";
- }
-
- $wps_target = 0 if $wps_target_param < 0;
-
- ($c, $w, $s, $bwwps) =
- find_saturation_point($wrk, $r, 0,
- (@args, @xargs));
-
- if ($wps_target_param < 0) {
- $wps_target = $s / -$wps_target_param;
-
- ($c, $w, $s, $bwwps) =
- find_saturation_point($wrk, $r,
- 0,
- (@args,
- @xargs));
- }
-
- if ($multi_mode and $w_direct) {
- my $widx;
-
- die unless scalar(@multi_workloads) ==
- scalar(@{$bwwps});
- die unless scalar(@multi_workloads) ==
- scalar(keys %allwps);
-
- # Total of all workload wps from the
- # mixed run.
- $w = 0;
- foreach $widx (0..$#{$bwwps}) {
- $w += $bwwps->[$widx];
- }
-
- # Total of all workload wps from when
- # ran individually with the best
- # balancer.
- my $tot = 0;
- foreach my $wrk (@multi_workloads) {
- $tot += $allwps{$wrk}->{$best_bid{$wrk}};
- }
-
- # Normalize mixed sum with sum of
- # individual runs.
- $w *= 100;
- $w /= $tot;
-
- # Second metric is average of each
- # workload wps normalized by their
- # individual run performance with the
- # best balancer.
- $s = 0;
- $widx = 0;
- foreach my $wrk (@multi_workloads) {
- $s += 100 * $bwwps->[$widx] /
- $allwps{$wrk}->{$best_bid{$wrk}};
- $widx++;
- }
- $s /= scalar(@multi_workloads);
-
- say sprintf('Aggregate (normalized) %.2f%%; fairness %.2f%%',
- $w, $s);
- } else {
- $allwps{$wrk} = \%wps;
- }
-
- $wps{$bid} = $w;
- $cwps{$bid} = $s;
-
- if ($realtime_target > 0 || $wps_target_param > 0) {
- $mwps{$bid} = $w * $c;
- } else {
- $mwps{$bid} = $w + $s;
- }
-
- say "$c clients ($w wps, $s wps single client, score=$mwps{$bid})."
- unless $multi_mode and $w_direct;
-
- last BAL unless $should_b;
- next BAL if $bal eq '';
- next GBAL if exists $bal_skip_H{$bal};
- }
- }
- }
-
- $widx++;
-
- @sorted = sort { $mwps{$b} <=> $mwps{$a} } keys %mwps;
- $best_bid{$wrk} = $sorted[0];
- @sorted = sort { $b <=> $a } values %mwps;
- $range = 1 - $sorted[-1] / $sorted[0];
- $best_bal{$wrk} = $sorted[0];
-
- next if $multi_mode and not $w_direct;
-
- say " Best balancer is '$best_bid{$wrk}' (range=$range).";
-
-
- $results{$wrk} = \%mwps;
-
- add_points(\%wps, \%scores, \%wscores);
- add_points(\%mwps, \%mscores, \%mwscores);
- add_points(\%cwps, \%cscores, \%cwscores);
-}
-
-sub dump_scoreboard
-{
- my ($n, $h) = @_;
- my ($i, $str, $balancer);
- my ($max, $range);
- my @sorted;
-
- @sorted = sort { $b <=> $a } values %{$h};
- $max = $sorted[0];
- $range = 1 - $sorted[-1] / $max;
- $str = "$n rank (range=$range):";
- say "\n$str";
- say '=' x length($str);
- $i = 1;
- foreach my $w (sort { $h->{$b} <=> $h->{$a} } keys %{$h}) {
- my $score;
-
- $balancer = $w if $i == 1;
- $score = $h->{$w} / $max;
-
- say " $i: '$w' ($score)";
-
- $i = $i + 1;
- }
-
- return $balancer;
-}
-
-dump_scoreboard($multi_mode ? 'Throughput' : 'Total wps', \%scores);
-dump_scoreboard('Total weighted wps', \%wscores) unless $multi_mode;
-dump_scoreboard($multi_mode ? 'Fairness' : 'Per client wps', \%cscores);
-dump_scoreboard('Per client weighted wps', \%cwscores) unless $multi_mode;
-$balancer = dump_scoreboard($multi_mode ? 'Combined' : 'Combined wps', \%mscores);
-$balancer = dump_scoreboard('Combined weighted wps', \%mwscores) unless $multi_mode;
-
-VERIFY:
-
-my %problem_wrk;
-
-die unless defined $balancer;
-
-say "\nBalancer is '$balancer'.";
-say "Idleness tolerance is $idle_tolerance_pct%.";
-
-if ($multi_mode) {
- $w_direct = '-w ' . join ' -w ', map("$wrk_root/$_", @workloads);
- @workloads = ($w_direct);
-}
-
-foreach my $wrk (@workloads) {
- my @args = ( "-n $nop" );
- my ($r, $error, $c, $wps, $swps);
- my $saturated = 0;
- my $result = 'Pass';
- my $vcs2 = $gt2 ? '1:0' : '2:1';
- my %problem;
- my $engines;
-
- next if not defined $w_direct and not can_balance_workload($wrk);
-
- push @args, $balancer unless $balancer eq '<none>';
-
- if (scalar(keys %results)) {
- $r = $results{$wrk}->{$balancer} / $best_bal{$wrk} * 100.0;
- } else {
- $r = '---';
- }
- say " \nProfiling '$wrk' ($r% of best)...";
-
- ($r, $error) = calibrate_workload($wrk);
- say " ${client_target_s}s is $r workloads. (error=$error)";
-
- ($c, $wps, $swps) = find_saturation_point($wrk, $r, $verbose, @args);
- say " Saturation at $c clients ($wps workloads/s).";
- push @args, "-c $c";
-
- $engines = trace_workload($wrk, $balancer, $r, $c);
-
- foreach my $key (keys %{$engines}) {
- next if $key eq 'gpu';
- $saturated = $saturated + 1
- if $engines->{$key} < $idle_tolerance_pct;
- }
-
- if ($saturated == 0) {
- # Not a single saturated engine
- $result = 'FAIL';
- } elsif (not exists $engines->{'2:0'} or not exists $engines->{$vcs2}) {
- # VCS1 and VCS2 not present in a balancing workload
- $result = 'FAIL';
- } elsif ($saturated == 1 and
- ($engines->{'2:0'} < $idle_tolerance_pct or
- $engines->{$vcs2} < $idle_tolerance_pct)) {
- # Only one VCS saturated
- $result = 'WARN';
- }
-
- $result = 'WARN' if $engines->{'gpu'} > $idle_tolerance_pct;
-
- if ($result ne 'Pass') {
- $problem{'c'} = $c;
- $problem{'r'} = $r;
- $problem{'stats'} = $engines;
- $problem_wrk{$wrk} = \%problem;
- }
-
- print " $result [";
- print map " $_: $engines->{$_}%,", sort keys %{$engines};
- say " ]";
-}
-
-say "\nProblematic workloads were:" if scalar(keys %problem_wrk) > 0;
-foreach my $wrk (sort keys %problem_wrk) {
- my $problem = $problem_wrk{$wrk};
-
- print " $wrk -c $problem->{'c'} -r $problem->{'r'} [";
- print map " $_: $problem->{'stats'}->{$_}%,",
- sort keys %{$problem->{'stats'}};
- say " ]";
-}