summaryrefslogtreecommitdiff
path: root/scripts/media-bench.pl
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/media-bench.pl')
-rwxr-xr-xscripts/media-bench.pl506
1 files changed, 506 insertions, 0 deletions
diff --git a/scripts/media-bench.pl b/scripts/media-bench.pl
new file mode 100755
index 00000000..cdbf6a19
--- /dev/null
+++ b/scripts/media-bench.pl
@@ -0,0 +1,506 @@
+#! /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 $show_cmds = 0;
+my $realtime_target = 0;
+my $wps_target = 0;
+my $balancer;
+my $nop;
+my %opts;
+
+my @balancers = ( 'rr', 'rand', 'qd', 'qdr', 'qdavg', 'rt', 'rtr', 'rtavg' );
+my %bal_skip_H = ( 'rr' => 1, 'rand' => 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_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 p.$period";
+
+ return @args;
+}
+
+sub run_workload
+{
+ my (@args) = @_;
+ my ($time, $wps, $cmd);
+
+ @args = add_wps_arg(@args);
+
+ 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;
+ }
+ }
+ close WSIM;
+
+ return ($time, $wps);
+}
+
+sub trace_workload
+{
+ my ($wrk, $b, $r, $c) = @_;
+ my @args = ( "-n $nop", "-w $wrk_root/$wrk", $balancer, "-r $r", "-c $c");
+ my $min_batches = 16 + $r * $c / 2;
+ my @skip_engine;
+ my %engines;
+ my $cmd;
+
+ unshift @args, '-q';
+ unshift @args, "$tracepl --trace $wsim";
+ $cmd = join ' ', @args;
+ show_cmd($cmd);
+ system($cmd);
+
+ $cmd = "perf script | $tracepl";
+ show_cmd($cmd);
+ open CMD, "$cmd |" or die;
+ while (<CMD>) {
+ chomp;
+ if (/Ring(\d+): (\d+) batches.*?(\d+\.?\d+)% idle,/) {
+ if ($2 >= $min_batches) {
+ $engines{$1} = $3;
+ } else {
+ push @skip_engine, $1;
+ }
+ }
+ }
+ close CMD;
+
+ $cmd = "perf script | $tracepl --html -x ctxsave -s --squash-ctx-id ";
+ $cmd .= join ' ', map("-i $_", @skip_engine);
+ $wrk =~ s/ /_/g;
+ $b =~ s/ /_/g;
+ $cmd .= " > ${wrk}_${b}_-r${r}_-c${c}.html";
+ show_cmd($cmd);
+ system($cmd);
+
+ return \%engines;
+}
+
+sub calibrate_workload
+{
+ my ($wrk) = @_;
+ my $tol = $tolerance;
+ my $loops = 0;
+ my $error;
+ my $r;
+
+ $r = 23;
+ for (;;) {
+ my @args = ( "-n $nop", "-w $wrk_root/$wrk", "-r $r");
+ my ($time, $wps);
+
+ ($time, $wps) = run_workload(@args);
+
+ $error = abs($time - $client_target_s) / $client_target_s;
+
+ last if $error <= $tol;
+
+ $r = int($wps * $client_target_s);
+ $loops = $loops + 1;
+ if ($loops >= 4) {
+ $tol = $tol * (1.0 + ($tol));
+ $loops = 0;
+ }
+ last if $tol > 0.2;
+ }
+
+ return ($r, $error);
+}
+
+sub find_saturation_point
+{
+ my (@args) = @_;
+ my ($last_wps, $c, $swps);
+
+ for ($c = 1; ; $c = $c + 1) {
+ my ($time, $wps);
+
+ ($time, $wps) = run_workload((@args, ("-c $c")));
+
+ if ($c > 1) {
+ my $error = abs($wps - $last_wps) / $last_wps;
+ last if $wps_target <= 0 and
+ ($wps < $last_wps or $error <= $tolerance);
+ last if $wps_target > 0 and $wps / $c < $wps_target;
+ } elsif ($c == 1) {
+ $swps = $wps;
+ }
+
+ $last_wps = $wps;
+ }
+
+ return ($c - 1, $last_wps, $swps);
+}
+
+getopts('hxn:b:W:B:r:t:i:R:T:', \%opts);
+
+if (defined $opts{'h'}) {
+ print <<ENDHELP;
+Supported options:
+
+ -h Help text.
+ -x Show external commands.
+ -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.
+ENDHELP
+ exit 0;
+}
+
+$show_cmds = $opts{'x'} if defined $opts{'x'};
+$balancer = $opts{'b'} if defined $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'};
+
+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;
+$nop = $opts{'n'};
+$nop = calibrate_nop() unless $nop;
+say "Nop calibration is $nop.";
+
+goto VERIFY if defined $balancer;
+
+my %best_bal;
+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;
+ }
+}
+
+foreach my $wrk (@workloads) {
+ my ($r, $error, $should_b, $best);
+ my (%wps, %cwps, %mwps);
+ my @sorted;
+ my @args;
+
+ $should_b = can_balance_workload($wrk);
+
+ print "\nEvaluating '$wrk'...";
+
+ ($r, $error) = calibrate_workload($wrk);
+ say " ${client_target_s}s is $r workloads. (error=$error)";
+ @args = ( "-n $nop", "-w $wrk_root/$wrk", "-r $r");
+
+ say " Finding saturation points for '$wrk'...";
+
+ BAL: foreach my $bal (@balancers) {
+ RBAL: foreach my $R ('', '-R') {
+ foreach my $H ('', '-H') {
+ my @xargs;
+ my ($w, $c, $s);
+ my $bid;
+
+ if ($bal ne '') {
+ push @xargs, "-b $bal";
+ push @xargs, '-R' if $R ne '';
+ push @xargs, '-H' if $H ne '';
+ $bid = join ' ', @xargs;
+ print " $bal balancer ('$bid'): ";
+ } else {
+ $bid = '<none>';
+ print " No balancing: ";
+ }
+
+ ($c, $w, $s) = find_saturation_point((@args,
+ @xargs));
+
+ $wps{$bid} = $w;
+ $cwps{$bid} = $s;
+
+ if ($realtime_target > 0 || $wps_target > 0) {
+ $mwps{$bid} = $w * $c;
+ } else {
+ $mwps{$bid} = $w * $w / $s + $s;
+ }
+
+ say "$c clients ($w wps, $s wps single client, score=$mwps{$bid}).";
+
+ last BAL unless $should_b;
+ next BAL if $bal eq '';
+ next RBAL if exists $bal_skip_H{$bal};
+ }
+ }
+ }
+
+ @sorted = sort { $mwps{$b} <=> $mwps{$a} } keys %mwps;
+ $best = $sorted[0];
+ @sorted = sort { $b <=> $a } values %mwps;
+ $best_bal{$wrk} = $sorted[0] if not exists $best_bal{$wrk} or
+ $sorted[0] > $best_bal{$wrk};
+ say " Best balancer is '$best' ($sorted[0]).";
+
+
+ $results{$wrk} = \%mwps;
+
+ add_points(\%wps, \%scores, \%wscores);
+ add_points(\%cwps, \%cscores, \%cwscores);
+ add_points(\%mwps, \%mscores, \%mwscores);
+}
+
+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 = $max - $sorted[-1];
+ $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('Total wps', \%scores);
+dump_scoreboard('Total weighted wps', \%wscores);
+dump_scoreboard('Per client wps', \%cscores);
+dump_scoreboard('Per client weighted wps', \%cwscores);
+dump_scoreboard('Combined wps', \%mscores);
+$balancer = dump_scoreboard('Combined weighted wps', \%mwscores);
+
+VERIFY:
+
+my %problem_wrk;
+
+die unless defined $balancer;
+
+say "\nBalancer is '$balancer'.";
+say "Idleness tolerance is $idle_tolerance_pct%.";
+
+foreach my $wrk (@workloads) {
+ my @args = ( "-n $nop", "-w $wrk_root/$wrk", $balancer);
+ my ($r, $error, $c, $wps, $swps);
+ my $saturated = 0;
+ my $result = 'Pass';
+ my %problem;
+ my $engines;
+
+ next unless can_balance_workload($wrk);
+
+ 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)";
+ push @args, "-r $r";
+
+ ($c, $wps, $swps) = find_saturation_point(@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}) {
+ $saturated = $saturated + 1
+ if $engines->{$key} < $idle_tolerance_pct;
+ }
+
+ if ($saturated == 0 or
+ ($saturated == 1 and
+ ($engines->{'2'} < $idle_tolerance_pct or
+ $engines->{'3'} < $idle_tolerance_pct))) {
+ $result = $saturated == 0 ? 'FAIL' : 'WARN';
+ $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 " ]";
+}