[cage] Unbreak the build
[parrot.git] / tools / dev / parrotbench.pl
blobbba4e3a4e7394e67bf4cc7249d26158fbf800c3d
1 #! perl
3 # Copyright (C) 2004-2007, Parrot Foundation.
4 # $Id$
6 use strict;
7 use warnings;
8 use Config::IniFiles;
9 use File::Basename;
10 use File::Find;
11 use File::Spec;
12 use FindBin;
13 use Getopt::Long;
14 use Pod::Usage;
15 require POSIX;
17 =head1 NAME
19 parrotbench - Parrot benchmark
21 =head1 SYNOPSIS
23 parrotbench.pl [options]
25 Options:
26 -b -benchmarks use benchmarks matching regexes (multiple)
27 -c -conf path to configuration file
28 -d -directory path to benchmarks directory
29 -h -? -help display this help and exits
30 -list list available benchmarks and exits
31 -m -method method of time from times()
32 1 $cuser + $csystem from times() (default)
33 2 Real time using POSIX::times()
34 -n -nobench skip benchmarks matching regexes (multiple)
35 -time show times instead of percentage
37 =head1 DESCRIPTION
39 Benchmark Parrot against other interpreters.
41 =head1 CONFIGURATION
43 You must specify paths to executables in a configuration file.
44 That file may be placed as parrotbench.conf in the same directory
45 as parrotbench.pl or otherwise explicitly specified with the
46 -conf option. You may set any command line option in the file with
47 the exception of the configuration file name itself. In the event
48 you have specified an option both in the configuration file and the
49 command line, the command line takes precedence.
51 Here is an example parrotbench.conf:
52 [global]
53 directory = ../../examples/benchmarks
54 list = 0
55 help = 0
56 method = 2
57 time = 1
59 [regexes]
60 include = ^gc
61 include = ^oo
62 exclude = header
63 exclude = waves
65 [benchmark parrotj]
66 exe = ../../parrot -R jit
67 type = .pasm
68 type = .pir
70 [benchmark perl_585_th]
71 exe = /usr/bin/perl585-th
72 type = .pl
74 [benchmark python]
75 exe = /usr/local/bin/python
76 type = .py
78 [benchmark ruby]
79 exe = /usr/bin/ruby
80 type = .rb
82 =head1 BUGS
84 While every effort was made to ensure this script is portable,
85 it is likely that it will break somewhere.
87 If a benchmark has multiple extensions associated with the same
88 executable, the last one will be used. For instance, with the
89 configuration file above, foo.pir would be selected over foo.pasm
91 =head1 AUTHOR
93 Joshua Gatcomb, C<Limbic_Region_2000@Yahoo.com>
95 Originally written by:
97 Sebastian Riedel, C<sri@oook.de>
99 =cut
101 # Create Default Configuration
102 my %cfg = (
103 config_file => File::Spec->catdir( $FindBin::Bin, 'parrotbench.conf' ),
104 bench_path => undef,
105 list_only => undef,
106 use_times => undef,
107 display_help => undef,
108 method => undef,
109 run_bench => [],
110 skip_bench => [],
113 # Read Command Line Options
114 GetOptions(
115 'conf=s' => \$cfg{config_file},
116 'directory=s' => \$cfg{bench_path},
117 'list' => \$cfg{list_only},
118 'time' => \$cfg{use_times},
119 'help|?' => \$cfg{display_help},
120 'method=s' => \$cfg{method},
121 'benchmarks=s' => $cfg{run_bench},
122 'nobench=s' => $cfg{skip_bench},
125 # Read Configuration File
126 die 'Unable to access configuration file ', $cfg{config_file} unless -r $cfg{config_file};
128 my $ini = Config::IniFiles->new( -file => $cfg{config_file} );
130 # Merge Configuration
131 if ( !defined $cfg{bench_path} ) {
132 $cfg{bench_path} = $ini->val( global => 'directory' );
134 if ( !defined $cfg{list_only} ) {
135 $cfg{list_only} = $ini->val( global => 'list' );
137 if ( !defined $cfg{use_times} ) {
138 $cfg{use_times} = $ini->val( global => 'time' );
140 if ( !defined $cfg{display_help} ) {
141 $cfg{display_help} = $ini->val( global => 'help' );
144 pod2usage 1 if $cfg{display_help};
146 if ( !defined $cfg{method} ) {
147 $cfg{method} = $ini->val( global => 'method', 1 );
150 if ( !@{ $cfg{run_bench} } ) {
151 my @regexes = grep defined, $ini->val( regexes => 'include' );
152 @{ $cfg{run_bench} } = @regexes ? @regexes : '[\d\D]';
154 if ( !@{ $cfg{skip_bench} } ) {
155 my @regexes = grep defined, $ini->val( regexes => 'exclude' );
156 @{ $cfg{skip_bench} } = @regexes ? @regexes : '[^\d\D]';
159 # Frequently Used Variables
160 my %bench;
161 my @section = sort $ini->GroupMembers('benchmark');
162 my @program = map { /^benchmark\s+(.*)$/ } @section;
163 my %suffix;
164 $suffix{$_} = [ map quotemeta, $ini->val( $_, 'type' ) ] for @section;
165 my $ticks = POSIX::sysconf(&POSIX::_SC_CLK_TCK);
166 my %Get_Time = (
167 1 => sub { my @times = times(); return $times[2] + $times[3] },
168 2 => sub { return ( POSIX::times() )[0] / $ticks },
171 # Find And Build Benchmarks
172 find sub {
173 my $pass;
174 for my $regex ( @{ $cfg{run_bench} } ) {
175 $pass++ and last if /$regex/;
177 return if !$pass;
178 my $fail;
179 for my $regex ( @{ $cfg{skip_bench} } ) {
180 $fail++ and last if /$regex/;
182 return if $fail;
183 for my $index ( 0 .. $#section ) {
184 my ( $name, $p, $ext ) = fileparse( $_, @{ $suffix{ $section[$index] } } );
185 next if !$ext;
186 $bench{$name}{ $program[$index] } = $ext;
188 }, $cfg{bench_path};
189 die "No benchmarks found" if !keys %bench;
191 # List Names Of Benchmarks With Pretty Output
192 if ( $cfg{list_only} ) {
193 my @rows;
194 push @rows, [ 'Benchmark', @program ];
195 for my $name ( sort keys %bench ) {
196 push @rows, [ $name, map { $bench{$name}{$_} || '-' } @program ];
198 my @max;
199 for ( 0 .. @program ) {
200 for my $row (@rows) {
201 Longest( $max[$_], length $row->[$_] );
204 for my $col (@rows) {
205 print map { sprintf( "%-$max[$_]s ", $col->[$_] ) } 0 .. $#$col;
206 print "\n";
208 exit;
211 # Run The Benchmarks With Pretty Output
212 if ( !$cfg{use_times} && @program < 2 ) {
213 print "WARNING: Switching percentage to time - not enough executables\n";
214 $cfg{use_times} = 1;
216 if ( $cfg{use_times} ) {
217 my $type = $cfg{method} == 1 ? 'CPU' : 'wall-clock';
218 print "Times are in $type seconds. (lower is better)\n";
220 else {
221 print "Numbers are relative to the first one. (lower is better)\n";
223 print "\n";
225 open( my $COPYOUT, ">&STDOUT" ) or die "Unable to copy STDOUT";
226 open( STDOUT, '>', File::Spec->devnull ) or die "Unable to redirect STDOUT";
227 select $COPYOUT;
228 $| = 1;
230 my @max = $cfg{method} == 1 ? (5) x @program : (6) x @program;
231 Longest( $max[0], length $_ ) for 'Benchmark', keys %bench;
232 Longest( $max[ $_ + 1 ], length $program[$_] ) for 0 .. $#program;
233 printf( "%-$max[0]s ", 'Benchmark' );
234 printf( "%-$max[$_ + 1]s ", $program[$_] ) for 0 .. $#program;
236 for my $name ( sort keys %bench ) {
237 my $base = 0;
238 printf( "\n%-$max[0]s ", $name );
239 for ( 0 .. $#section ) {
240 my ( $prog, $sect ) = ( $program[$_], $section[$_] );
241 if ( $bench{$name}{$prog} ) {
242 my $start = $Get_Time{ $cfg{method} }->();
243 system( $ini->val( $sect, 'exe' ) . " "
244 . File::Spec->catdir( $cfg{bench_path}, $name . $bench{$name}{$prog} ) );
245 my $stop = $Get_Time{ $cfg{method} }->();
246 my $used = $stop - $start;
247 $base ||= $used;
248 printf( "%-$max[$_ + 1]s ",
249 $cfg{use_times}
250 ? sprintf( "%.3f", $used )
251 : sprintf( "%d%%", $used / ( $base / 100 ) ) );
253 else {
254 printf( "%-$max[$_ + 1]s ", '-' );
259 sub Longest {
260 $_[0] = $_[1] and return if !defined $_[0];
261 $_[0] = $_[1] if $_[1] > $_[0];
264 # Local Variables:
265 # mode: cperl
266 # cperl-indent-level: 4
267 # fill-column: 100
268 # End:
269 # vim: expandtab shiftwidth=4: