kernel: scheduling fix for ARM
[minix.git] / benchmarks / unixbench-5.1.2 / Run
blobd5279a62aa060a62dad82b53a89962966280fc1c
1 #!/usr/pkg/bin/perl -w
3 use strict;
5 use POSIX qw(strftime);
6 use Time::HiRes;
7 use IO::Handle;
10 ############################################################################
11 # UnixBench - Release 5.1.1, based on:
12 # The BYTE UNIX Benchmarks - Release 3
13 # Module: Run SID: 3.11 5/15/91 19:30:14
14 # Original Byte benchmarks written by:
15 # Ben Smith, Tom Yager at BYTE Magazine
16 # ben@bytepb.byte.com tyager@bytepb.byte.com
17 # BIX: bensmith tyager
19 #######################################################################
20 # General Purpose Benchmark
21 # based on the work by Ken McDonell, Computer Science, Monash University
23 # You will need ...
24 # perl Time::HiRes IO::Handlecat cc chmod comm cp date dc df echo
25 # kill ls make mkdir rm sed test time touch tty umask who
26 ###############################################################################
27 # Modification Log:
28 # $Header: run,v 5.2 88/01/12 06:23:43 kenj Exp $
29 # Ken McDonell, Computer Science, Monash University
30 # August 1, 1983
31 # 3/89 - Ben Smith - BYTE: globalized many variables, modernized syntax
32 # 5/89 - commented and modernized. Removed workload items till they
33 # have been modernized. Added database server test.
34 # 11/14/89 - Made modifications to reflect new version of fstime
35 # and elimination of mem tests.
36 # 10/22/90 - Many tests have been flipped so that they run for
37 # a specified length of time and loops are counted.
38 # 4/3/91 - Cleaned up and debugged several test parameters - Ben
39 # 4/9/91 - Added structure for creating index and determing flavor of UNIX
40 # 4/26/91 - Made changes and corrections suggested by Tin Le of Sony
41 # 5/15/91 - Removed db from distribution
42 # 4/4/92 Jon Tombs <jon@robots.ox.ac.uk> fixed for GNU time to look like
43 # BSD (don't know the format of sysV!)
44 # 12/95 - Massive changes for portability, speed, and more meaningful index
45 # DCN David C Niemi <niemi@tux.org>
46 # 1997.06.20 DCN Fixed overflow condition in fstime.c on fast machines
47 # 1997.08.24 DCN Modified "system", replaced double with
48 # whetstone-double in "index"
49 # 1997.09.10 DCN Added perlbench as an Exhibition benchmark
50 # 1997.09.23 DCN Added rgooch's select as an Exhibition benchmark
51 # 1999.07.28 DCN "select" not compiled or run by default, because it
52 # does not compile on many platforms. PerlBench also
53 # not run by default.
54 # 2007.09.26 IS Huge rewrite -- see release notes in README.
55 # 2007.10.12 IS Added graphics tests, categories feature.
56 # 2007.10.14 IS Set and report LANG. Added "grep" and "sysexec".
57 # 2007.12.22 IS Tiny fixes; see README.
60 ############################################################################
61 # CONFIGURATION
62 ############################################################################
64 # Version number of the script.
65 my $version = "5.1.2";
67 # The setting of LANG makes a huge difference to some of the scores,
68 # particularly depending on whether UTF-8 is used. So we always set
69 # it to the same value, which is configured here.
71 # If you want your results to be meaningful when compared to other peoples'
72 # results, you should not change this. Change it if you want to measure the
73 # effect of different languages.
74 my $language = "en_US.utf8";
76 # The number of iterations per test.
77 my $longIterCount = 10;
78 my $shortIterCount = 3;
80 # C compiler to use in compilation tests.
81 my $cCompiler = $ENV{CC};
83 # Establish full paths to directories. These need to be full pathnames
84 # (or do they, any more?). They can be set in env.
85 # variables whose names are the first parameter to getDir() below.
86 my $BASEDIR = `pwd`;
87 chomp($BASEDIR);
89 # Directory where the test programs live.
90 my $BINDIR = getDir('UB_BINDIR', $BASEDIR . "/pgms");
92 # Temp directory, for temp files.
93 my $TMPDIR = getDir('UB_TMPDIR', $BASEDIR . "/tmp");
95 # Directory to put results in.
96 my $RESULTDIR = getDir('UB_RESULTDIR', $BASEDIR . "/results");
98 # Directory where the tests are executed.
99 my $TESTDIR = getDir('UB_TESTDIR', $BASEDIR . "/testdir");
102 ############################################################################
103 # TEST SPECIFICATIONS
104 ############################################################################
106 # Configure the categories to which tests can belong.
107 my $testCats = {
108 'system' => { 'name' => "System Benchmarks", 'maxCopies' => 16 },
109 '2d' => { 'name' => "2D Graphics Benchmarks", 'maxCopies' => 1 },
110 '3d' => { 'name' => "3D Graphics Benchmarks", 'maxCopies' => 1 },
111 'misc' => { 'name' => "Non-Index Benchmarks", 'maxCopies' => 16 },
115 my $arithmetic = [
116 "arithoh", "short", "int", "long", "float", "double", "whetstone-double"
119 my $fs = [
120 "fstime-w", "fstime-r", "fstime",
121 "fsbuffer-w", "fsbuffer-r", "fsbuffer",
122 "fsdisk-w", "fsdisk-r", "fsdisk"
125 my $oldsystem = [
126 "execl", "fstime", "fsbuffer", "fsdisk", "pipe", "context1", "spawn",
127 "syscall"
130 my $system = [
131 @$oldsystem, "shell1", "shell8" # , "shell16"
134 my $index = [
135 "dhry2reg", "whetstone-double", @$oldsystem, "shell1", "shell8"
138 my $graphics = [
139 "2d-rects", "2d-ellipse", "2d-aashapes", "2d-text", "2d-blit",
140 "2d-window", "ubgears"
144 # List of all supported test names.
145 my $testList = {
146 # Individual tests.
147 "dhry2reg" => undef,
148 "whetstone-double" => undef,
149 "syscall" => undef,
150 "pipe" => undef,
151 "context1" => undef,
152 "spawn" => undef,
153 "execl" => undef,
154 "fstime-w" => undef,
155 "fstime-r" => undef,
156 "fstime" => undef,
157 "fsbuffer-w" => undef,
158 "fsbuffer-r" => undef,
159 "fsbuffer" => undef,
160 "fsdisk-w" => undef,
161 "fsdisk-r" => undef,
162 "fsdisk" => undef,
163 "shell1" => undef,
164 "shell8" => undef,
165 "shell16" => undef,
166 "short" => undef,
167 "int" => undef,
168 "long" => undef,
169 "float" => undef,
170 "double" => undef,
171 "arithoh" => undef,
172 "C" => undef,
173 "dc" => undef,
174 "hanoi" => undef,
175 "grep" => undef,
176 "sysexec" => undef,
178 "2d-rects" => undef,
179 "2d-lines" => undef,
180 "2d-circle" => undef,
181 "2d-ellipse" => undef,
182 "2d-shapes" => undef,
183 "2d-aashapes" => undef,
184 "2d-polys" => undef,
185 "2d-text" => undef,
186 "2d-blit" => undef,
187 "2d-window" => undef,
189 "ubgears" => undef,
191 # Named combos and shorthands.
192 "arithmetic" => $arithmetic,
193 "dhry" => [ "dhry2reg" ],
194 "dhrystone" => [ "dhry2reg" ],
195 "whets" => [ "whetstone-double" ],
196 "whetstone" => [ "whetstone-double" ],
197 "load" => [ "shell" ],
198 "misc" => [ "C", "dc", "hanoi" ],
199 "speed" => [ @$arithmetic, @$system ],
200 "oldsystem" => $oldsystem,
201 "system" => $system,
202 "fs" => $fs,
203 "shell" => [ "shell1", "shell8" ],
204 "graphics" => $graphics,
206 # The tests which constitute the official index.
207 "index" => $index,
209 # The tests which constitute the official index plus the graphics
210 # index.
211 "gindex" => [ @$index, @$graphics ],
215 # Default parameters for benchmarks. Note that if "prog" is used,
216 # it must contain just the program name, as it will be quoted (this
217 # is necessary if BINDIR contains spaces). Put any options in "options".
218 my $baseParams = {
219 "prog" => undef,
220 "options" => "",
221 "repeat" => 'short',
222 "stdout" => 1, # Non-0 to keep stdout.
223 "stdin" => "",
224 "logmsg" => "",
228 # Individual parameters for all benchmarks.
229 my $testParams = {
231 ##########################
232 ## System Benchmarks ##
233 ##########################
235 "dhry2reg" => {
236 "logmsg" => "Dhrystone 2 using register variables",
237 "cat" => 'system',
238 "options" => "10",
239 "repeat" => 'long',
241 "whetstone-double" => {
242 "logmsg" => "Double-Precision Whetstone",
243 "cat" => 'system',
244 "repeat" => 'long',
246 "syscall" => {
247 "logmsg" => "System Call Overhead",
248 "cat" => 'system',
249 "repeat" => 'long',
250 "options" => "10",
252 "context1" => {
253 "logmsg" => "Pipe-based Context Switching",
254 "cat" => 'system',
255 "repeat" => 'long',
256 "options" => "10",
258 "pipe" => {
259 "logmsg" => "Pipe Throughput",
260 "cat" => 'system',
261 "repeat" => 'long',
262 "options" => "10",
264 "spawn" => {
265 "logmsg" => "Process Creation",
266 "cat" => 'system',
267 "options" => "30",
269 "execl" => {
270 "logmsg" => "Execl Throughput",
271 "cat" => 'system',
272 "options" => "30",
274 "fstime-w" => {
275 "logmsg" => "File Write 1024 bufsize 2000 maxblocks",
276 "cat" => 'system',
277 "prog" => "${BINDIR}/fstime",
278 "options" => "-w -t 30 -d \"${TMPDIR}\" -b 1024 -m 2000",
280 "fstime-r" => {
281 "logmsg" => "File Read 1024 bufsize 2000 maxblocks",
282 "cat" => 'system',
283 "prog" => "${BINDIR}/fstime",
284 "options" => "-r -t 30 -d \"${TMPDIR}\" -b 1024 -m 2000",
286 "fstime" => {
287 "logmsg" => "File Copy 1024 bufsize 2000 maxblocks",
288 "cat" => 'system',
289 "prog" => "${BINDIR}/fstime",
290 "options" => "-c -t 30 -d \"${TMPDIR}\" -b 1024 -m 2000",
292 "fsbuffer-w" => {
293 "logmsg" => "File Write 256 bufsize 500 maxblocks",
294 "cat" => 'system',
295 "prog" => "${BINDIR}/fstime",
296 "options" => "-w -t 30 -d \"${TMPDIR}\" -b 256 -m 500",
298 "fsbuffer-r" => {
299 "logmsg" => "File Read 256 bufsize 500 maxblocks",
300 "cat" => 'system',
301 "prog" => "${BINDIR}/fstime",
302 "options" => "-r -t 30 -d \"${TMPDIR}\" -b 256 -m 500",
304 "fsbuffer" => {
305 "logmsg" => "File Copy 256 bufsize 500 maxblocks",
306 "cat" => 'system',
307 "prog" => "${BINDIR}/fstime",
308 "options" => "-c -t 30 -d \"${TMPDIR}\" -b 256 -m 500",
310 "fsdisk-w" => {
311 "logmsg" => "File Write 4096 bufsize 8000 maxblocks",
312 "cat" => 'system',
313 "prog" => "${BINDIR}/fstime",
314 "options" => "-w -t 30 -d \"${TMPDIR}\" -b 4096 -m 8000",
316 "fsdisk-r" => {
317 "logmsg" => "File Read 4096 bufsize 8000 maxblocks",
318 "cat" => 'system',
319 "prog" => "${BINDIR}/fstime",
320 "options" => "-r -t 30 -d \"${TMPDIR}\" -b 4096 -m 8000",
322 "fsdisk" => {
323 "logmsg" => "File Copy 4096 bufsize 8000 maxblocks",
324 "cat" => 'system',
325 "prog" => "${BINDIR}/fstime",
326 "options" => "-c -t 30 -d \"${TMPDIR}\" -b 4096 -m 8000",
328 "shell1" => {
329 "logmsg" => "Shell Scripts (1 concurrent)",
330 "cat" => 'system',
331 "prog" => "${BINDIR}/looper",
332 "options" => "60 \"${BINDIR}/multi.sh\" 1",
334 "shell8" => {
335 "logmsg" => "Shell Scripts (8 concurrent)",
336 "cat" => 'system',
337 "prog" => "${BINDIR}/looper",
338 "options" => "60 \"${BINDIR}/multi.sh\" 8",
340 "shell16" => {
341 "logmsg" => "Shell Scripts (16 concurrent)",
342 "cat" => 'system',
343 "prog" => "${BINDIR}/looper",
344 "options" => "60 \"${BINDIR}/multi.sh\" 16",
347 ##########################
348 ## Graphics Benchmarks ##
349 ##########################
351 "2d-rects" => {
352 "logmsg" => "2D graphics: rectangles",
353 "cat" => '2d',
354 "prog" => "${BINDIR}/gfx-x11",
355 "options" => "rects 3 2",
358 "2d-lines" => {
359 "logmsg" => "2D graphics: lines",
360 "cat" => '2d',
361 "prog" => "${BINDIR}/gfx-x11",
362 "options" => "lines 3 2",
365 "2d-circle" => {
366 "logmsg" => "2D graphics: circles",
367 "cat" => '2d',
368 "prog" => "${BINDIR}/gfx-x11",
369 "options" => "circle 3 2",
372 "2d-ellipse" => {
373 "logmsg" => "2D graphics: ellipses",
374 "cat" => '2d',
375 "prog" => "${BINDIR}/gfx-x11",
376 "options" => "ellipse 3 2",
379 "2d-shapes" => {
380 "logmsg" => "2D graphics: polygons",
381 "cat" => '2d',
382 "prog" => "${BINDIR}/gfx-x11",
383 "options" => "shapes 3 2",
386 "2d-aashapes" => {
387 "logmsg" => "2D graphics: aa polygons",
388 "cat" => '2d',
389 "prog" => "${BINDIR}/gfx-x11",
390 "options" => "aashapes 3 2",
393 "2d-polys" => {
394 "logmsg" => "2D graphics: complex polygons",
395 "cat" => '2d',
396 "prog" => "${BINDIR}/gfx-x11",
397 "options" => "polys 3 2",
400 "2d-text" => {
401 "logmsg" => "2D graphics: text",
402 "cat" => '2d',
403 "prog" => "${BINDIR}/gfx-x11",
404 "options" => "text 3 2",
407 "2d-blit" => {
408 "logmsg" => "2D graphics: images and blits",
409 "cat" => '2d',
410 "prog" => "${BINDIR}/gfx-x11",
411 "options" => "blit 3 2",
414 "2d-window" => {
415 "logmsg" => "2D graphics: windows",
416 "cat" => '2d',
417 "prog" => "${BINDIR}/gfx-x11",
418 "options" => "window 3 2",
421 "ubgears" => {
422 "logmsg" => "3D graphics: gears",
423 "cat" => '3d',
424 "options" => "-time 20 -v",
428 ##########################
429 ## Non-Index Benchmarks ##
430 ##########################
432 "C" => {
433 "logmsg" => "C Compiler Throughput ($cCompiler)",
434 "cat" => 'misc',
435 "prog" => "${BINDIR}/looper",
436 "options" => "60 $cCompiler cctest.c",
438 "arithoh" => {
439 "logmsg" => "Arithoh",
440 "cat" => 'misc',
441 "options" => "10",
443 "short" => {
444 "logmsg" => "Arithmetic Test (short)",
445 "cat" => 'misc',
446 "options" => "10",
448 "int" => {
449 "logmsg" => "Arithmetic Test (int)",
450 "cat" => 'misc',
451 "options" => "10",
453 "long" => {
454 "logmsg" => "Arithmetic Test (long)",
455 "cat" => 'misc',
456 "options" => "10",
458 "float" => {
459 "logmsg" => "Arithmetic Test (float)",
460 "cat" => 'misc',
461 "options" => "10",
463 "double" => {
464 "logmsg" => "Arithmetic Test (double)",
465 "cat" => 'misc',
466 "options" => "10",
468 "dc" => {
469 "logmsg" => "Dc: sqrt(2) to 99 decimal places",
470 "cat" => 'misc',
471 "prog" => "${BINDIR}/looper",
472 "options" => "30 dc",
473 "stdin" => "dc.dat",
475 "hanoi" => {
476 "logmsg" => "Recursion Test -- Tower of Hanoi",
477 "cat" => 'misc',
478 "options" => "20",
480 "grep" => {
481 "logmsg" => "Grep a large file (system's grep)",
482 "cat" => 'misc',
483 "prog" => "${BINDIR}/looper",
484 "options" => "30 grep -c gimp large.txt",
486 "sysexec" => {
487 "logmsg" => "Exec System Call Overhead",
488 "cat" => 'misc',
489 "repeat" => 'long',
490 "prog" => "${BINDIR}/syscall",
491 "options" => "10 exec",
496 # CPU flags of interest.
497 my $x86CpuFlags = {
498 'pae' => "Physical Address Ext",
499 'sep' => "SYSENTER/SYSEXIT",
500 'syscall' => "SYSCALL/SYSRET",
501 'mmx' => "MMX",
502 'mmxext' => "AMD MMX",
503 'cxmmx' => "Cyrix MMX",
504 'xmm' => "Streaming SIMD",
505 'xmm2' => "Streaming SIMD-2",
506 'xmm3' => "Streaming SIMD-3",
507 'ht' => "Hyper-Threading",
508 'ia64' => "IA-64 processor",
509 'lm' => "x86-64",
510 'vmx' => "Intel virtualization",
511 'svm' => "AMD virtualization",
515 ############################################################################
516 # UTILITIES
517 ############################################################################
519 # Exec the given command, and catch its standard output.
520 # We return an array containing the PID and the filehandle on the
521 # process' standard output. It's up to the caller to wait for the command
522 # to terminate.
523 sub command {
524 my ( $cmd ) = @_;
526 my $pid = open(my $childFd, "-|");
527 if (!defined($pid)) {
528 die("Run: fork() failed (undef)\n");
529 } elsif ($pid == 0) {
530 exec($cmd);
531 die("Run: exec() failed (returned)\n");
534 return ( $pid, $childFd );
538 # Get data from running a system command. Used for things like getting
539 # the host OS from `uname -o` etc.
541 # Ignores initial blank lines from the command and returns the first
542 # non-blank line, with white space trimmed off. Returns a blank string
543 # if there is no output; undef if the command fails.
544 sub getCmdOutput {
545 my ( $cmd ) = @_;
547 my ( $pid, $fd ) = command($cmd . " 2>/dev/null");
548 my $result = "";
549 while (<$fd>) {
550 chomp;
551 next if /^[ \t]*$/;
553 $result = $_;
554 $result =~ s/^[ \t]+//;
555 $result =~ s/[ \t]+$//;
556 last;
559 # Close the command and wait for it to die.
560 waitpid($pid, 0);
561 my $status = $?;
563 return $status == 0 ? $result : undef;
567 # Get a directory pathname from an environment variable, or the given
568 # default. Canonicalise and return the value.
569 sub getDir {
570 my ( $var, $def ) = @_;
572 my $val = $ENV{$var} || $def;
574 # Canonicalise the value.
575 my $wd;
576 chomp($wd = `pwd`);
577 chdir($val);
578 chomp($val = `pwd`);
579 chdir($wd);
580 $ENV{$var} = $val;
582 $val;
586 # Get the name of the file we're going to log to. The name uses the hostname
587 # and date, plus a sequence number to make it unique.
588 sub logFile {
589 my ( $sysInfo ) = @_;
591 my $count = 1;
593 # Use the date in the base file name.
594 my $ymd = strftime "%Y-%m-%d", localtime;
596 while (1) {
597 my $log = sprintf "%s/%s-%s-%02d",
598 ${RESULTDIR}, $sysInfo->{'name'}, $ymd, $count;
599 return $log if (! -e $log);
600 ++$count;
605 # Print a message to the named log file. We use this method rather than
606 # keeping the FD open because we use shell redirection to send command
607 # output to the same file.
608 sub printLog {
609 my ( $logFile, @args ) = @_;
611 open(my $fd, ">>", $logFile) || abortRun("can't append to $logFile");
612 printf $fd @args;
613 close($fd);
617 # Display a number of something, auto-selecting the plural form
618 # if appropriate. We are given the number, the singular, and the
619 # plural; if the plural is omitted, it defaults to singular + "s".
620 sub number {
621 my ( $n, $what, $plural ) = @_;
623 $plural = $what . "s" if !defined($plural);
625 if (!defined($n)) {
626 return sprintf "unknown %s", $plural;
627 } else {
628 return sprintf "%d %s", $n, $n == 1 ? $what : $plural;
633 # Merge two sets of test parameters -- defaults and actual parameters.
634 # Return the merged parameter hash.
635 sub mergeParams {
636 my ( $def, $vals ) = @_;
638 my $params = { };
639 foreach my $k (keys(%$def)) {
640 $params->{$k} = $def->{$k};
642 foreach my $k (keys(%$vals)) {
643 $params->{$k} = $vals->{$k};
646 $params;
650 ############################################################################
651 # SYSTEM ANALYSIS
652 ############################################################################
654 # Extract interesting flags from the given processor flags string and
655 # convert them to descriptive names.
656 sub processCpuFlags {
657 my ( $flagStr ) = @_;
659 my @names;
660 foreach my $f (sort split(/\s+/, $flagStr)) {
661 my $name = $x86CpuFlags->{$f};
662 push(@names, $name) if $name;
665 join(", ", @names);
669 # Get information on the CPUs in the system. Returns a reference to an
670 # array of N entries, one per CPU, where each entry is a hash containing
671 # these fields:
672 # describing the model etc. Returns undef if the information can't be got.
673 sub getCpuInfo {
674 open(my $fd, "<", "/proc/cpuinfo") || return undef;
676 my $cpus = [ ];
677 my $cpu = 0;
678 while (<$fd>) {
679 chomp;
680 my ( $field, $val ) = split(/[ \t]*:[ \t]*/);
681 next if (!$field || !$val);
682 if ($field eq "processor") {
683 $cpu = $val;
684 } elsif ($field eq "model name") {
685 my $model = $val;
686 $model =~ s/ +/ /g;
687 $cpus->[$cpu]{'model'} = $model;
688 } elsif ($field eq "bogomips") {
689 $cpus->[$cpu]{'bogo'} = $val;
690 } elsif ($field eq "flags") {
691 $cpus->[$cpu]{'flags'} = processCpuFlags($val);
695 close($fd);
697 $cpus;
701 # Get information on the host system. Returns a reference to a hash
702 # with the following fields:
703 # name Host name
704 # os Host OS name
705 # osRel Host OS release
706 # osVer Host OS version
707 # mach Host machine name (eg. "SparcStation 20", but on
708 # PC/Linux usually "i686" etc.)
709 # platform Hardware platform; on Linux, the base CPU type?
710 # system System name (eg. hostname and Linux distro, like
711 # "hostname: openSUSE 10.2 (i586)").
712 # cpus Value returned by getCpuInfo(), undef if not avail.
713 # numCpus Number of CPUs if known, else undef.
714 # load System load message as per "uptime".
715 # numUsers Number of users and/or open shell sessions.
716 sub getSystemInfo {
717 my $info = { };
719 # Get host system data.
720 if ($ENV{MINIX}) {
721 $info->{'name'} = getCmdOutput("uname -a");
722 } else {
723 $info->{'name'} = getCmdOutput("hostname");
725 $info->{'os'} = getCmdOutput("uname -o") || getCmdOutput("uname -s");
726 $info->{'osRel'} = getCmdOutput("uname -r");
727 $info->{'osVer'} = getCmdOutput("uname -v");
728 $info->{'mach'} = getCmdOutput("uname -m");
729 if (!$ENV{MINIX}) {
730 $info->{'platform'} = getCmdOutput("uname -i");
733 # Get the system name (SUSE, Red Hat, etc.) if possible.
734 $info->{'system'} = $info->{'os'};
735 if ( -r "/etc/SuSE-release" ) {
736 $info->{'system'} = getCmdOutput("cat /etc/SuSE-release");
737 } elsif ( -r "/etc/release" ) {
738 $info->{'system'} = getCmdOutput("cat /etc/release");
741 # Get the language info.
742 if (!$ENV{MINIX}) {
743 my $lang = getCmdOutput("printenv LANG");
744 my $map = getCmdOutput("locale -k LC_CTYPE | grep charmap");
745 $map =~ s/.*=//;
746 my $coll = getCmdOutput("locale -k LC_COLLATE | grep collate-codeset");
747 $coll =~ s/.*=//;
748 $info->{'language'} = sprintf "%s (charmap=%s, collate=%s)",
749 $lang, $map, $coll;
752 # Get details on the CPUs, if possible.
753 my $cpus = getCpuInfo();
754 if (defined($cpus)) {
755 $info->{'cpus'} = $cpus;
756 $info->{'numCpus'} = scalar(@$cpus);
759 # Get graphics hardware info.
760 # if (!$ENV{MINIX}) {
761 # $info->{'graphics'} = getCmdOutput("3dinfo | cut -f1 -d\'(\'");
764 # Get system run state, load and usage info.
765 if (!$ENV{MINIX}) {
766 $info->{'runlevel'} = getCmdOutput("runlevel | cut -f2 -d\" \"");
768 $info->{'load'} = getCmdOutput("uptime");
769 $info->{'numUsers'} = getCmdOutput("who | wc -l");
771 $info;
775 ############################################################################
776 # ERROR HANDLING
777 ############################################################################
779 # Abort the benchmarking run with an error message.
780 sub abortRun {
781 my ( $err ) = @_;
783 printf STDERR "\n**********************************************\n";
784 printf STDERR "Run: %s; aborting\n", $err;
785 exit(1);
789 ############################################################################
790 # TEST SETUP
791 ############################################################################
793 # Do checks that everything's ready for testing.
794 sub preChecks {
795 # Set the language.
796 $ENV{'LANG'} = $language;
798 # Check that the required files are in the proper places.
799 system("make check");
800 if ($? != 0) {
801 system("make all");
802 if ($? != 0) {
803 abortRun("\"make all\" failed");
807 # Create a script to kill this run.
808 system("echo \"kill -9 $$\" > \"${TMPDIR}/kill_run\"");
809 chmod(0755, $TMPDIR . "/kill_run");
813 # Parse the command arguments.
814 sub parseArgs {
815 my @words = @_;
817 # The accumulator for the bench units to be run.
818 my $tests = [ ];
819 my $params = { 'tests' => $tests };
821 # Generate the requested list of bench programs.
822 my $opt;
823 my $word;
824 while ($word = shift(@words)) {
825 if ($word !~ m/^-/) { # A test name.
826 if ($word eq "all") {
827 foreach my $t (keys(%$testList)) {
828 push(@$tests, $t) if (!defined($testList->{$t}));
830 } elsif (exists($testList->{$word})) {
831 my $val = $testList->{$word} || [ $word ];
832 push(@$tests, @$val);
833 } else {
834 die("Run: unknown test \"$word\"\n");
836 } elsif ($word eq "-q") {
837 $params->{'verbose'} = 0;
838 } elsif ($word eq "-v") {
839 $params->{'verbose'} = 2;
840 } elsif ($word eq "-i") {
841 $params->{'iterations'} = shift(@words);
842 } elsif ($word eq "-c") {
843 if (!defined($params->{'copies'})) {
844 $params->{'copies'} = [ ];
846 push(@{$params->{'copies'}}, shift(@words));
847 } else {
848 die("Run: unknown option $word\n");
852 $params;
856 ############################################################################
857 # RESULTS INPUT / OUTPUT
858 ############################################################################
860 # Read a set of benchmarking results from the given file.
861 # Returns results in the form returned by runTests(), but without the
862 # individual pass results.
863 sub readResultsFromFile {
864 my ( $file ) = @_;
866 # Attempt to get the baseline data file; if we can't, just return undef.
867 open(my $fd, "<", $file) || return undef;
869 my $results = { };
870 while (<$fd>) {
871 chomp;
873 # Dump comments, ignore blank lines.
874 s/#.*//;
875 next if /^\s*$/;
877 my ( $name, $time, $slab, $sum, $score, $iters ) = split(/\|/);
878 my $bresult = { };
879 $bresult->{'score'} = $score;
880 $bresult->{'scorelabel'} = $slab;
881 $bresult->{'time'} = $time;
882 $bresult->{'iterations'} = $iters;
884 $results->{$name} = $bresult;
887 close($fd);
889 $results;
893 ############################################################################
894 # RESULTS PROCESSING
895 ############################################################################
897 # Process a set of results from a single test by averaging the individal
898 # pass results into a single final value.
899 # First, though, dump the worst 1/3 of the scores. The logic is that a
900 # glitch in the system (background process waking up, for example) may
901 # make one or two runs go slow, so let's discard those.
903 # $bresult is a hashed array representing the results of a single test;
904 # $bresult->{'passes'} is an array of the output from the individual
905 # passes.
906 sub combinePassResults {
907 my ( $bench, $tdata, $bresult, $logFile ) = @_;
909 $bresult->{'cat'} = $tdata->{'cat'};
911 # Computed results.
912 my $iterations = 0;
913 my $totalTime = 0;
914 my $sum = 0;
915 my $product = 0;
916 my $label;
918 my $pres = $bresult->{'passes'};
920 # We're going to throw away the worst 1/3 of the pass results.
921 # Figure out how many to keep.
922 my $npasses = scalar(@$pres);
923 my $ndump = int($npasses / 3);
925 foreach my $presult (sort { $a->{'COUNT0'} <=> $b->{'COUNT0'} } @$pres) {
926 my $count = $presult->{'COUNT0'};
927 my $timebase = $presult->{'COUNT1'};
928 $label = $presult->{'COUNT2'};
929 my $time = $presult->{'TIME'} || $presult->{'elapsed'};
931 # Skip this result if it's one of the worst ones.
932 if ($ndump > 0) {
933 printLog($logFile, "*Dump score: %12.1f\n", $count);
934 --$ndump;
935 next;
938 # Count this result.
939 ++$iterations;
940 printLog($logFile, "Count score: %12.1f\n", $count);
942 # If $timebase is 0 the figure is a rate; else compute
943 # counts per $timebase. $time is always seconds.
944 if ($timebase > 0) {
945 $sum += $count / ($time / $timebase);
946 $product += log($count) - log($time / $timebase);
947 } else {
948 $sum += $count;
949 $product += log($count);
951 $totalTime += $time;
954 # Save the results for the benchmark.
955 if ($iterations > 0) {
956 $bresult->{'score'} = exp($product / $iterations);
957 $bresult->{'scorelabel'} = $label;
958 $bresult->{'time'} = $totalTime / $iterations;
959 $bresult->{'iterations'} = $iterations;
960 } else {
961 $bresult->{'error'} = "No measured results";
966 # Index the given full benchmark results against the baseline results.
967 # $results is a hashed array of test names to test results.
969 # Adds the following fields to each benchmark result:
970 # iscore The baseline score for this test
971 # index The index of this test against the baseline
972 # Adds the following fields to $results:
973 # indexed The number of tests for which index values were
974 # generated
975 # fullindex Non-0 if all the index tests were indexed
976 # index The computed overall index for the run
977 # Note that the index values are computed as
978 # result / baseline * 10
979 # so an index of 523 indicates that a test ran 52.3 times faster than
980 # the baseline.
981 sub indexResults {
982 my ( $results ) = @_;
984 # Read in the baseline result data. If we can't get it, just return
985 # without making indexed results.
986 my $index = readResultsFromFile($BINDIR . "/index.base");
987 if (!defined($index)) {
988 return;
991 # Count the number of results we have (indexed or not) in
992 # each category.
993 my $numCat = { };
994 foreach my $bench (@{$results->{'list'}}) {
995 my $bresult = $results->{$bench};
996 ++$numCat->{$bresult->{'cat'}};
998 $results->{'numCat'} = $numCat;
1000 my $numIndex = { };
1001 my $indexed = { };
1002 my $sum = { };
1003 foreach my $bench (sort(keys(%$index))) {
1004 # Get the test data for this benchmark.
1005 my $tdata = $testParams->{$bench};
1006 if (!defined($tdata)) {
1007 abortRun("unknown benchmark \"$bench\" in $BINDIR/index.base");
1010 # Get the test category. Count the total tests in this cat.
1011 my $cat = $tdata->{'cat'};
1012 ++$numIndex->{$cat};
1014 # If we don't have a result for this test, skip.
1015 next if (!defined($results->{$bench}));
1017 # Get the index and actual results. Calcluate the score.
1018 my $iresult = $index->{$bench};
1019 my $bresult = $results->{$bench};
1020 my $ratio = $bresult->{'score'} / $iresult->{'score'};
1022 # Save the indexed score.
1023 $bresult->{'iscore'} = $iresult->{'score'};
1024 $bresult->{'index'} = $ratio * 10;
1026 # Sun the scores, and count this test for this category.
1027 $sum->{$cat} += log($ratio);
1028 ++$indexed->{$cat};
1031 # Calculate the index scores per category.
1032 $results->{'indexed'} = $indexed;
1033 $results->{'numIndex'} = $numIndex;
1034 foreach my $c (keys(%$indexed)) {
1035 if ($indexed->{$c} > 0) {
1036 $results->{'index'}{$c} = exp($sum->{$c} / $indexed->{$c}) * 10;
1042 ############################################################################
1043 # TEST EXECUTION
1044 ############################################################################
1046 # Exec the given command in a sub-process.
1048 # In the child process, we run the command and store its standard output.
1049 # We also time its execution, and catch its exit status. We then write
1050 # the command's output, plus lines containing the execution time and status,
1051 # to a pipe.
1053 # In the parent process, we immediately return an array containing the
1054 # child PID and the filehandle to the pipe. This allows the caller to
1055 # kick off multiple commands in parallel, then gather their output.
1056 sub commandBuffered {
1057 my ( $cmd ) = @_;
1059 # Create a pipe for parent-child communication.
1060 my $childReader;
1061 my $parentWriter;
1062 pipe($childReader, $parentWriter) || abortRun("pipe() failed");
1063 $parentWriter->autoflush(1);
1065 # Fork off the child process.
1066 my $pid = fork();
1067 if (!defined($pid)) {
1068 abortRun("fork() failed (undef)");
1069 } elsif ($pid == 0) {
1070 # Close the other end of the pipe.
1071 close $childReader;
1073 # Start the clock and spawn the command.
1074 my $benchStart = Time::HiRes::time();
1075 my ( $cmdPid, $cmdFd ) = command($cmd);
1077 # Read and buffer all the command's output.
1078 my $output = [ ];
1079 while (<$cmdFd>) {
1080 push(@$output, $_);
1083 # Stop the clock and save the time.
1084 my $elTime = Time::HiRes::time() - $benchStart;
1085 push(@$output, sprintf "elapsed|%f\n", $elTime);
1087 # Wait for the child to die so we can get its status.
1088 # close($cmdFd); Doesn't work???
1089 waitpid($cmdPid, 0);
1090 my $status = $?;
1091 push(@$output, sprintf "status|%d\n", $status);
1093 # Now that we've got the time, play back all the output to the pipe.
1094 # The parent can read this at its leisure.
1095 foreach my $line (@$output) {
1096 print $parentWriter $line;
1099 # Terminate this child.
1100 close $parentWriter;
1101 exit(0);
1104 # Close the other end of the pipe.
1105 close $parentWriter;
1107 return ( $pid, $childReader );
1111 # Read the results of a benchmark execution from a child process, given
1112 # its process ID and its filehandle. Create a results hash structure
1113 # containing the fields returned by the child, plus:
1114 # pid The child's process ID
1115 # status The child's exit status
1116 # ERROR Any stderr output from the child that isn't result data
1117 # Note that ay result fields with ultiple values are split; so eg.
1118 # COUNT|x|y|x
1119 # becomes
1120 # COUNT0 = x
1121 # COUNT1 = y
1122 # COUNT2 = z
1123 sub readResults {
1124 my ( $pid, $fd ) = @_;
1126 my $presult = { 'pid' => $pid };
1128 # Read all the result lines from the child.
1129 while (<$fd>) {
1130 chomp;
1132 my ( $field, @params ) = split(/\|/);
1133 if (scalar(@params) == 0) { # Error message.
1134 $presult->{'ERROR'} .= "\n" if ($presult->{'ERROR'});
1135 $presult->{'ERROR'} .= $field;
1136 } elsif (scalar(@params) == 1) { # Simple data.
1137 $presult->{$field} = $params[0];
1138 } else { # Compound data.
1139 # Store the values in separate fields, named "FIELD$i".
1140 for (my $x = 0; $x < scalar(@params); ++$x) {
1141 $presult->{$field . $x} = $params[$x];
1146 # If the command had an error, make an appropriate message if we
1147 # don't have one.
1148 if ($presult->{'status'} != 0 && !defined($presult->{'ERROR'})) {
1149 $presult->{'ERROR'} = "command returned status " . $presult->{'status'};
1152 # Wait for the child to die.
1153 close($fd);
1154 waitpid($pid, 0);
1156 $presult;
1160 # Execute a benchmark command. We set off a given number of copies in
1161 # parallel to exercise multiple CPUs.
1163 # We return an array of results hashes, one per copy; each one is as
1164 # returned by readResults().
1165 sub executeBenchmark {
1166 my ( $command, $copies ) = @_;
1168 # Array of contexts for all the copies we're running.
1169 my $ctxt = [ ];
1171 # Kick off all the commands at once.
1172 for (my $i = 0; $i < $copies; ++$i) {
1173 my ( $cmdPid, $cmdFd ) = commandBuffered($command);
1174 $ctxt->[$i] = {
1175 'pid' => $cmdPid,
1176 'fd' => $cmdFd,
1180 # Now, we can simply read back the command results in order. Because
1181 # the child processes read and buffer the results and time the commands,
1182 # there's no need to use select() to read the results as they appear.
1183 my $pres = [ ];
1184 for (my $i = 0; $i < $copies; ++$i) {
1185 my $presult = readResults($ctxt->[$i]{'pid'}, $ctxt->[$i]{'fd'});
1186 push(@$pres, $presult);
1189 $pres;
1193 # Run one iteration of a benchmark, as specified by the given
1194 # benchmark parameters. We run multiple parallel copies as
1195 # specified by $copies.
1196 sub runOnePass {
1197 my ( $params, $verbose, $logFile, $copies ) = @_;
1199 # Get the command to run.
1200 my $command = $params->{'command'};
1201 if ($verbose > 1) {
1202 printf "\n";
1203 printf "COMMAND: \"%s\"\n", $command;
1204 printf "COPIES: \"%d\"\n", $copies;
1207 # Remember where we are, and move to the test directory.
1208 my $pwd = `pwd`;
1209 chdir($TESTDIR);
1211 # Execute N copies of the benchmark in parallel.
1212 my $copyResults = executeBenchmark($command, $copies);
1213 printLog($logFile, "\n");
1215 # Move back home.
1216 chdir($pwd);
1218 # Sum up the scores of the copies.
1219 my $count = 0;
1220 my $time = 0;
1221 my $elap = 0;
1222 foreach my $res (@$copyResults) {
1223 # Log the result data for each copy.
1224 foreach my $k (sort(keys(%$res))) {
1225 printLog($logFile, "# %s: %s\n", $k, $res->{$k});
1227 printLog($logFile, "\n");
1229 # If it failed, bomb out.
1230 if (defined($res->{'ERROR'})) {
1231 my $name = $params->{'logmsg'};
1232 abortRun("\"$name\": " . $res->{'ERROR'});
1235 # Count up the score.
1236 $count += $res->{'COUNT0'};
1237 $time += $res->{'TIME'} || $res->{'elapsed'};
1238 $elap += $res->{'elapsed'};
1241 # Make up a combined result.
1242 my $passResult = $copyResults->[0];
1243 $passResult->{'COUNT0'} = $count;
1244 $passResult->{'TIME'} = $time / $copies;
1245 $passResult->{'elapsed'} = $elap / $copies;
1247 $passResult;
1251 sub runBenchmark {
1252 my ( $bench, $tparams, $verbose, $logFile, $copies ) = @_;
1254 # Make up the actual benchmark parameters.
1255 my $params = mergeParams($baseParams, $tparams);
1257 # Make up the command string based on the parameters.
1258 my $prog = $params->{'prog'} || $BINDIR . "/" . $bench;
1259 my $command = sprintf "\"%s\" %s", $prog, $params->{'options'};
1260 $command .= " < \"" . $params->{'stdin'} . "\"" if ($params->{'stdin'});
1261 $command .= " 2>&1";
1262 $command .= $params->{'stdout'} ? (" >> \"" . $logFile . "\"") : " > /dev/null";
1263 $params->{'command'} = $command;
1265 # Set up the benchmark results structure.
1266 my $bresult = { 'name' => $bench, 'msg' => $params->{'logmsg'} };
1268 if ($verbose > 0) {
1269 printf "\n%d x %s ", $copies, $params->{'logmsg'};
1272 printLog($logFile,
1273 "\n########################################################\n");
1274 printLog($logFile, "%s -- %s\n",
1275 $params->{'logmsg'}, number($copies, "copy", "copies"));
1276 printLog($logFile, "==> %s\n\n", $command);
1278 # Run the test iterations, as given by the "repeat" parameter.
1279 my $repeats = $shortIterCount;
1280 $repeats = $longIterCount if $params->{'repeat'} eq 'long';
1281 $repeats = 1 if $params->{'repeat'} eq 'single';
1282 my $pres = [ ];
1283 for (my $i = 1; $i <= $repeats; ++$i) {
1284 printLog($logFile, "#### Pass %d\n\n", $i);
1286 # make an attempt to flush buffers
1287 system("sync; sleep 1; sync; sleep 2");
1288 # display heartbeat
1289 if ($verbose > 0) {
1290 printf " %d", $i;
1293 # Execute one pass of the benchmark.
1294 my $presult = runOnePass($params, $verbose, $logFile, $copies);
1295 push(@$pres, $presult);
1297 $bresult->{'passes'} = $pres;
1299 # Calculate the averaged results for this benchmark.
1300 combinePassResults($bench, $tparams, $bresult, $logFile);
1302 # Log the results.
1303 if ($copies == 1) {
1304 printLog($logFile, "\n>>>> Results of 1 copy\n");
1305 } else {
1306 printLog($logFile, "\n>>>> Sum of %d copies\n", $copies);
1308 foreach my $k ( 'score', 'time', 'iterations' ) {
1309 printLog($logFile, ">>>> %s: %s\n", $k, $bresult->{$k});
1311 printLog($logFile, "\n");
1313 # Some specific cleanup routines.
1314 if ($bench eq "C") {
1315 unlink(${TESTDIR} . "/cctest.o");
1316 unlink(${TESTDIR} . "/a.out");
1319 if ($verbose > 0) {
1320 printf "\n";
1323 $bresult;
1327 # Run the named benchmarks.
1328 sub runTests {
1329 my ( $tests, $verbose, $logFile, $copies ) = @_;
1331 # Run all the requested tests and gather the results.
1332 my $results = { 'start' => time(), 'copies' => $copies };
1333 foreach my $bench (@$tests) {
1334 # Get the parameters for this benchmark.
1335 my $params = $testParams->{$bench};
1336 if (!defined($params)) {
1337 abortRun("unknown benchmark \"$bench\"");
1340 # If the benchmark doesn't want to run with this many copies, skip it.
1341 my $cat = $params->{'cat'};
1342 my $maxCopies = $testCats->{$cat}{'maxCopies'};
1343 next if ($copies > $maxCopies);
1345 # Run the benchmark.
1346 my $bresult = runBenchmark($bench, $params, $verbose, $logFile, $copies);
1347 $results->{$bench} = $bresult;
1349 $results->{'end'} = time();
1351 # Generate a sorted list of benchmarks for which we have results.
1352 my @benches = grep {
1353 ref($results->{$_}) eq "HASH" && defined($results->{$_}{'msg'})
1354 } keys(%$results);
1355 @benches = sort {
1356 $results->{$a}{'msg'} cmp $results->{$b}{'msg'}
1357 } @benches;
1358 $results->{'list'} = \@benches;
1360 # Generate index scores for the results relative to the baseline data.
1361 indexResults($results);
1363 $results;
1367 ############################################################################
1368 # TEXT REPORTS
1369 ############################################################################
1371 # Display a banner indicating the configuration of the system under test
1372 # to the given file desc.
1373 sub displaySystem {
1374 my ( $info, $fd ) = @_;
1376 # Display basic system info.
1377 printf $fd " System: %s: %s\n", $info->{'name'}, $info->{'system'};
1378 printf $fd " OS: %s -- %s -- %s\n",
1379 $info->{'os'}, $info->{'osRel'}, $info->{'osVer'};
1380 if (!$ENV{MINIX}) {
1381 printf $fd " Machine: %s (%s)\n", $info->{'mach'}, $info->{'platform'};
1383 printf $fd " Machine: %s\n", $info->{'mach'};
1384 if (!$ENV{MINIX}) {
1385 printf $fd " Language: %s\n", $info->{'language'};
1388 # Get and display details on the CPUs, if possible.
1389 my $cpus = $info->{'cpus'};
1390 if (!defined($cpus)) {
1391 printf $fd " CPU: no details available\n";
1392 } else {
1393 for (my $i = 0; $i <= $#$cpus; ++$i) {
1394 printf $fd " CPU %d: %s (%.1f bogomips)\n",
1395 $i, $cpus->[$i]{'model'}, $cpus->[$i]{'bogo'};
1396 printf $fd " %s\n", $cpus->[$i]{'flags'};
1400 # if (!$ENV{MINIX}) {
1401 # if ($info->{'graphics'}) {
1402 # printf $fd " Graphics: %s\n", $info->{'graphics'};
1406 # Display system load and usage info.
1407 if (!$ENV{MINIX}) {
1408 printf $fd " %s; runlevel %s\n\n", $info->{'load'}, $info->{'runlevel'};
1413 # Display the test scores from the given set of test results.
1414 sub logResults {
1415 my ( $results, $outFd ) = @_;
1417 # Display the individual test scores.
1418 foreach my $bench (@{$results->{'list'}}) {
1419 my $bresult = $results->{$bench};
1421 printf $outFd "%-40s %12.1f %-5s (%.1f s, %d samples)\n",
1422 $bresult->{'msg'},
1423 $bresult->{'score'},
1424 $bresult->{'scorelabel'},
1425 $bresult->{'time'},
1426 $bresult->{'iterations'};
1429 printf $outFd "\n";
1433 # Display index scores, if any, for the given run results.
1434 sub logIndexCat {
1435 my ( $results, $cat, $outFd ) = @_;
1437 my $total = $results->{'numIndex'}{$cat};
1438 my $indexed = $results->{'indexed'}{$cat};
1439 my $iscore = $results->{'index'}{$cat};
1440 my $full = $total == $indexed;
1442 # If there are no indexed scores, just say so.
1443 if (!defined($indexed) || $indexed == 0) {
1444 printf $outFd "No index results available for %s\n\n",
1445 $testCats->{$cat}{'name'};
1446 return;
1449 # Display the header, depending on whether we have a full set of index
1450 # scores, or a partial set.
1451 my $head = $testCats->{$cat}{'name'} .
1452 ($full ? " Index Values" : " Partial Index");
1453 printf $outFd "%-40s %12s %12s %8s\n",
1454 $head, "BASELINE", "RESULT", "INDEX";
1456 # Display the individual test scores.
1457 foreach my $bench (@{$results->{'list'}}) {
1458 my $bresult = $results->{$bench};
1459 next if $bresult->{'cat'} ne $cat;
1461 if (defined($bresult->{'iscore'}) && defined($bresult->{'index'})) {
1462 printf $outFd "%-40s %12.1f %12.1f %8.1f\n",
1463 $bresult->{'msg'}, $bresult->{'iscore'},
1464 $bresult->{'score'}, $bresult->{'index'};
1465 } else {
1466 printf $outFd "%-40s %12s %12.1f %8s\n",
1467 $bresult->{'msg'}, "---",
1468 $bresult->{'score'}, "---";
1472 # Display the overall score.
1473 my $title = $testCats->{$cat}{'name'} . " Index Score";
1474 if (!$full) {
1475 $title .= " (Partial Only)";
1477 printf $outFd "%-40s %12s %12s %8s\n", "", "", "", "========";
1478 printf $outFd "%-66s %8.1f\n", $title, $iscore;
1480 printf $outFd "\n";
1484 # Display index scores, if any, for the given run results.
1485 sub logIndex {
1486 my ( $results, $outFd ) = @_;
1488 my $count = $results->{'indexed'};
1489 foreach my $cat (keys(%$count)) {
1490 logIndexCat($results, $cat, $outFd);
1495 # Dump the given run results into the given report file.
1496 sub summarizeRun {
1497 my ( $systemInfo, $results, $verbose, $reportFd ) = @_;
1499 # Display information about this test run.
1500 printf $reportFd "------------------------------------------------------------------------\n";
1501 printf $reportFd "Benchmark Run: %s %s - %s\n",
1502 strftime("%a %b %d %Y", localtime($results->{'start'})),
1503 strftime("%H:%M:%S", localtime($results->{'start'})),
1504 strftime("%H:%M:%S", localtime($results->{'end'}));
1505 printf $reportFd "%s in system; running %s of tests\n",
1506 number($systemInfo->{'numCpus'}, "CPU"),
1507 number($results->{'copies'}, "parallel copy", "parallel copies");
1508 printf $reportFd "\n";
1510 # Display the run scores.
1511 logResults($results, $reportFd);
1513 # Display the indexed scores, if any.
1514 logIndex($results, $reportFd);
1518 ############################################################################
1519 # HTML REPORTS
1520 ############################################################################
1522 # Dump the given run results into the given report file.
1523 sub runHeaderHtml {
1524 my ( $systemInfo, $reportFd ) = @_;
1526 # Display information about this test run.
1527 my $title = sprintf "Benchmark of %s / %s on %s",
1528 $systemInfo->{'name'}, $systemInfo->{'system'},
1529 strftime("%a %b %d %Y", localtime());
1531 print $reportFd <<EOF;
1532 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
1533 "http://www.w3.org/TR/html4/loose.dtd">
1534 <html>
1535 <head>
1536 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
1537 <meta name="keywords" content="linux, benchmarks, benchmarking">
1538 <title>$title</title>
1539 <style type="text/css">
1540 table {
1541 margin: 1em 1em 1em 0;
1542 background: #f9f9f9;
1543 border: 1px #aaaaaa solid;
1544 border-collapse: collapse;
1547 table th, table td {
1548 border: 1px #aaaaaa solid;
1549 padding: 0.2em;
1552 table th {
1553 background: #f2f2f2;
1554 text-align: center;
1556 </style>
1557 </head>
1558 <body>
1561 # Display information about this test run.
1562 printf $reportFd "<h2>%s</h2>\n", $title;
1563 printf $reportFd "<p><b>BYTE UNIX Benchmarks (Version %s)</b></p>\n\n",
1564 $version;
1568 # Display a banner indicating the configuration of the system under test
1569 # to the given file desc.
1570 sub displaySystemHtml {
1571 my ( $info, $fd ) = @_;
1573 printf $fd "<h3>Test System Information</h3>\n";
1574 printf $fd "<p><table>\n";
1576 # Display basic system info.
1577 printf $fd "<tr>\n";
1578 printf $fd " <td><b>System:</b></td>\n";
1579 printf $fd " <td colspan=2>%s: %s</td>\n",
1580 $info->{'name'}, $info->{'system'};
1581 printf $fd "</tr><tr>\n";
1582 printf $fd " <td><b>OS:</b></td>\n";
1583 printf $fd " <td colspan=2>%s -- %s -- %s</td>\n",
1584 $info->{'os'}, $info->{'osRel'}, $info->{'osVer'};
1585 printf $fd "</tr><tr>\n";
1586 printf $fd " <td><b>Machine:</b></td>\n";
1587 if (!$ENV{MINIX}) {
1588 printf $fd " <td colspan=2>%s: %s</td>\n",
1589 $info->{'mach'}, $info->{'platform'};
1591 printf $fd " <td colspan=2>%s</td>\n",
1592 $info->{'mach'};
1593 if (!$ENV{MINIX}) {
1594 printf $fd "</tr><tr>\n";
1595 printf $fd " <td><b>Language:</b></td>\n";
1596 printf $fd " <td colspan=2>%s</td>\n", $info->{'language'};
1598 printf $fd "</tr>\n";
1600 # Get and display details on the CPUs, if possible.
1601 my $cpus = $info->{'cpus'};
1602 if (!defined($cpus)) {
1603 printf $fd "<tr>\n";
1604 printf $fd " <td><b>CPUs:</b></td>\n";
1605 printf $fd " <td colspan=2>no details available</td>\n";
1606 printf $fd "</tr>\n";
1607 } else {
1608 for (my $i = 0; $i <= $#$cpus; ++$i) {
1609 printf $fd "<tr>\n";
1610 if ($i == 0) {
1611 printf $fd " <td rowspan=%d><b>CPUs:</b></td>\n", $#$cpus + 1;
1613 printf $fd " <td><b>%d:</b></td>\n", $i;
1614 printf $fd " <td>%s (%.1f bogomips)<br/>\n",
1615 $cpus->[$i]{'model'}, $cpus->[$i]{'bogo'};
1616 printf $fd " %s</td>\n", $cpus->[$i]{'flags'};
1617 printf $fd "</tr>\n";
1621 # Display graphics hardware info.
1622 # if (!$ENV{MINIX}) {
1623 # if ($info->{'graphics'}) {
1624 # printf $fd "<tr>\n";
1625 # printf $fd " <td><b>Graphics:</b></td>\n";
1626 # printf $fd " <td colspan=2>%s</td>\n", $info->{'graphics'};
1627 # printf $fd "</tr>\n";
1631 # Display system runlevel, load and usage info.
1632 printf $fd "<tr>\n";
1633 printf $fd " <td><b>Uptime:</b></td>\n";
1634 if (!$ENV{MINIX}) {
1635 printf $fd " <td colspan=2>%s; runlevel %s</td>\n",
1636 $info->{'load'}, $info->{'runlevel'};
1638 printf $fd " <td colspan=2>%s\n",
1639 $info->{'load'};
1640 printf $fd "</tr>\n";
1642 printf $fd "</table></p>\n\n";
1646 # Display the test scores from the given set of test results
1647 # for a given category of tests.
1648 sub logCatResultsHtml {
1649 my ( $results, $cat, $fd ) = @_;
1651 my $numIndex = $results->{'numIndex'}{$cat};
1652 my $indexed = $results->{'indexed'}{$cat};
1653 my $iscore = $results->{'index'}{$cat};
1654 my $full = defined($indexed) && $indexed == $numIndex;
1656 # If there are no results in this category, just ignore it.
1657 if (!defined($results->{'numCat'}{$cat}) ||
1658 $results->{'numCat'}{$cat} == 0) {
1659 return;
1662 # Say the category. If there are no indexed scores, just say so.
1663 my $warn = "";
1664 if (!defined($indexed) || $indexed == 0) {
1665 $warn = " — no index results available";
1666 } elsif (!$full) {
1667 $warn = " — not all index tests were run;" .
1668 " only a partial index score is available";
1670 printf $fd "<h4>%s%s</h4>\n", $testCats->{$cat}{'name'}, $warn;
1672 printf $fd "<p><table width=\"100%%\">\n";
1674 printf $fd "<tr>\n";
1675 printf $fd " <th align=left>Test</th>\n";
1676 printf $fd " <th align=right>Score</th>\n";
1677 printf $fd " <th align=left>Unit</th>\n";
1678 printf $fd " <th align=right>Time</th>\n";
1679 printf $fd " <th align=right>Iters.</th>\n";
1680 printf $fd " <th align=right>Baseline</th>\n";
1681 printf $fd " <th align=right>Index</th>\n";
1682 printf $fd "</tr>\n";
1684 # Display the individual test scores.
1685 foreach my $bench (@{$results->{'list'}}) {
1686 my $bresult = $results->{$bench};
1687 next if $bresult->{'cat'} ne $cat;
1689 printf $fd "<tr>\n";
1690 printf $fd " <td><b>%s</b></td>\n", $bresult->{'msg'};
1691 printf $fd " <td align=right><tt>%.1f</tt></td>\n",
1692 $bresult->{'score'};
1693 printf $fd " <td align=left><tt>%s</tt></td>\n",
1694 $bresult->{'scorelabel'};
1695 printf $fd " <td align=right><tt>%.1f s</tt></td>\n",
1696 $bresult->{'time'};
1697 printf $fd " <td align=right><tt>%d</tt></td>\n",
1698 $bresult->{'iterations'};
1700 if (defined($bresult->{'index'})) {
1701 printf $fd " <td align=right><tt>%.1f</tt></td>\n",
1702 $bresult->{'iscore'};
1703 printf $fd " <td align=right><tt>%.1f</tt></td>\n",
1704 $bresult->{'index'};
1706 printf $fd "</tr>\n";
1709 # Display the overall score.
1710 if (defined($indexed) && $indexed > 0) {
1711 my $title = $testCats->{$cat}{'name'} . " Index Score";
1712 if (!$full) {
1713 $title .= " (Partial Only)";
1715 printf $fd "<tr>\n";
1716 printf $fd " <td colspan=6><b>%s:</b></td>\n", $title;
1717 printf $fd " <td align=right><b><tt>%.1f</tt></b></td>\n", $iscore;
1718 printf $fd "</tr>\n";
1721 printf $fd "</table></p>\n\n";
1725 # Display index scores, if any, for the given run results.
1726 sub logResultsHtml {
1727 my ( $results, $fd ) = @_;
1729 foreach my $cat (keys(%$testCats)) {
1730 logCatResultsHtml($results, $cat, $fd);
1735 # Dump the given run results into the given report file.
1736 sub summarizeRunHtml {
1737 my ( $systemInfo, $results, $verbose, $reportFd ) = @_;
1739 # Display information about this test run.
1740 my $time = $results->{'end'} - $results->{'start'};
1741 printf $reportFd "<p><hr/></p>\n";
1742 printf $reportFd "<h3>Benchmark Run: %s; %s</h3>\n",
1743 number($systemInfo->{'numCpus'}, "CPU"),
1744 number($results->{'copies'}, "parallel process", "parallel processes");
1745 printf $reportFd "<p>Time: %s - %s; %dm %02ds</p>\n",
1746 strftime("%H:%M:%S", localtime($results->{'start'})),
1747 strftime("%H:%M:%S", localtime($results->{'end'})),
1748 int($time / 60), $time % 60;
1749 printf $reportFd "\n";
1751 # Display the run scores.
1752 logResultsHtml($results, $reportFd);
1756 sub runFooterHtml {
1757 my ( $reportFd ) = @_;
1759 print $reportFd <<EOF;
1760 <p><hr/></p>
1761 <div><b>No Warranties:</b> This information is provided free of charge and "as
1762 is" without any warranty, condition, or representation of any kind,
1763 either express or implied, including but not limited to, any warranty
1764 respecting non-infringement, and the implied warranties of conditions
1765 of merchantability and fitness for a particular purpose. All logos or
1766 trademarks on this site are the property of their respective owner. In
1767 no event shall the author be liable for any
1768 direct, indirect, special, incidental, consequential or other damages
1769 howsoever caused whether arising in contract, tort, or otherwise,
1770 arising out of or in connection with the use or performance of the
1771 information contained on this web site.</div>
1772 </body>
1773 </html>
1778 ############################################################################
1779 # MAIN
1780 ############################################################################
1782 sub main {
1783 my @args = @_;
1785 my $params = parseArgs(@args);
1786 my $verbose = $params->{'verbose'} || 1;
1787 if ($params->{'iterations'}) {
1788 $longIterCount = $params->{'iterations'};
1789 $shortIterCount = int(($params->{'iterations'} + 1) / 3);
1790 $shortIterCount = 1 if ($shortIterCount < 1);
1793 # If no benchmark units have be specified, do "index".
1794 my $tests = $params->{'tests'};
1795 if ($#$tests < 0) {
1796 $tests = $index;
1799 preChecks();
1800 my $systemInfo = getSystemInfo();
1802 # If the number of copies to run was not set, set it to 1
1803 # and the number of CPUs in the system (if > 1).
1804 my $copies = $params->{'copies'};
1805 if (!$copies || scalar(@$copies) == 0) {
1806 push(@$copies, 1);
1807 if (defined($systemInfo->{'numCpus'}) && $systemInfo->{'numCpus'} > 1) {
1808 push(@$copies, $systemInfo->{'numCpus'});
1812 # Display the program banner.
1813 system("cat \"${BINDIR}/unixbench.logo\"");
1815 if ($verbose > 1) {
1816 printf "\n", join(", ", @$tests);
1817 printf "Tests to run: %s\n", join(", ", @$tests);
1820 # Generate unique file names for the report and log file.
1821 my $reportFile = logFile($systemInfo);
1822 my $reportHtml = $reportFile . ".html";
1823 my $logFile = $reportFile . ".log";
1825 # Open the log file for writing.
1826 open(my $reportFd, ">", $reportFile) ||
1827 die("Run: can't write to $reportFile\n");
1828 open(my $reportFd2, ">", $reportHtml) ||
1829 die("Run: can't write to $reportHtml\n");
1830 printf $reportFd " BYTE UNIX Benchmarks (Version %s)\n\n", $version;
1831 runHeaderHtml($systemInfo, $reportFd2);
1833 # Dump information about the system under test.
1834 displaySystem($systemInfo, $reportFd);
1835 displaySystemHtml($systemInfo, $reportFd2);
1837 # Run the tests! Do a test run once for each desired number of copies;
1838 # for example, on a 2-CPU system, we may do a single-processing run
1839 # followed by a dual-processing run.
1840 foreach my $c (@$copies) {
1841 if ($verbose > 1) {
1842 printf "Run with %s\n", number($c, "copy", "copies");
1844 my $results = runTests($tests, $verbose, $logFile, $c);
1846 summarizeRun($systemInfo, $results, $verbose, $reportFd);
1847 summarizeRunHtml($systemInfo, $results, $verbose, $reportFd2);
1850 runFooterHtml($reportFd2);
1852 # Finish the report.
1853 close($reportFd);
1854 close($reportFd2);
1856 # Display the report, if not in quiet mode.
1857 if ($verbose > 0) {
1858 printf "\n";
1859 printf "========================================================================\n";
1860 system("cat \"$reportFile\"");
1867 exit(main(@ARGV));