diff options
| author | Andreas Brachold <vdr07@deltab.de> | 2007-11-10 17:53:53 +0000 |
|---|---|---|
| committer | Andreas Brachold <vdr07@deltab.de> | 2007-11-10 17:53:53 +0000 |
| commit | cfdd733c17cfa4f1a43b827a656e9e53cc2524ac (patch) | |
| tree | b6f659b1281f77628b36768f0888f67b65f9ca48 /lib/Benchmark/Timer.pm | |
| parent | 9c6c30350161efd74faa3c3705096aecb71c0e81 (diff) | |
| download | xxv-cfdd733c17cfa4f1a43b827a656e9e53cc2524ac.tar.gz xxv-cfdd733c17cfa4f1a43b827a656e9e53cc2524ac.tar.bz2 | |
* Remove unsed packages
* Reorder exit routines
Diffstat (limited to 'lib/Benchmark/Timer.pm')
| -rwxr-xr-x | lib/Benchmark/Timer.pm | 618 |
1 files changed, 0 insertions, 618 deletions
diff --git a/lib/Benchmark/Timer.pm b/lib/Benchmark/Timer.pm deleted file mode 100755 index 624289b..0000000 --- a/lib/Benchmark/Timer.pm +++ /dev/null @@ -1,618 +0,0 @@ -# ======================================================================== -# Benchmark::Timer - Perl code benchmarking tool -# David Coppit <david@coppit.org> -# -# This program contains embedded documentation in Perl POD (Plain Old -# Documentation) format. Search for the string "=head1" in this document -# to find documentation snippets, or use "perldoc" to read it; utilities -# like "pod2man" and "pod2html" can reformat as well. -# -# Copyright(c) 2004 David Coppit -# Copyright(c) 2000-2001 Andrew Ho. -# -# ======================================================================== - -=head1 NAME - -Benchmark::Timer - Benchmarking with statistical confidence - -=head1 SYNOPSIS - - # Non-statistical usage - use Benchmark::Timer; - $t = Benchmark::Timer->new(skip => 1); - - for(1 .. 1000) { - $t->start('tag'); - &long_running_operation(); - $t->stop('tag'); - } - print $t->report; - - # -------------------------------------------------------------------- - - # Statistical usage - use Benchmark::Timer; - $t = Benchmark::Timer->new(skip => 1, confidence => 97.5, error => 2); - - while($t->need_more_samples('tag')) { - $t->start('tag'); - &long_running_operation(); - $t->stop('tag'); - } - print $t->report; - -=head1 DESCRIPTION - -The Benchmark::Timer class allows you to time portions of code -conveniently, as well as benchmark code by allowing timings of repeated -trials. It is perfect for when you need more precise information about the -running time of portions of your code than the Benchmark module will give -you, but don't want to go all out and profile your code. - -The methodology is simple; create a Benchmark::Timer object, and wrap portions -of code that you want to benchmark with C<start()> and C<stop()> method calls. -You can supply a tag to those methods if you plan to time multiple portions of -code. If you provide error and confidence values, you can also use -C<need_more_samples()> to determine, statistically, whether you need to -collect more data. - -After you have run your code, you can obtain information about the running -time by calling the C<results()> method, or get a descriptive benchmark report -by calling C<report()>. If you run your code over multiple trials, the -average time is reported. This is wonderful for benchmarking time-critical -portions of code in a rigorous way. You can also optionally choose to skip any -number of initial trials to cut down on initial case irregularities. - -=head1 METHODS - -In all of the following methods, C<$tag> refers to the user-supplied name of -the code being timed. Unless otherwise specified, $tag defaults to the tag of -the last call to C<start()>, or "_default" if C<start()> was not previously -called with a tag. - -=over 4 - -=cut - - -# ------------------------------------------------------------------------ -# Package setup - -package Benchmark::Timer; -require 5.005; -use strict; - -use Carp; -use Time::HiRes qw( gettimeofday tv_interval ); - -use vars qw($VERSION); -$VERSION = sprintf "%d.%02d%02d", q/0.71.0/ =~ /(\d+)/g; - -use constant BEFORE => 0; -use constant ELAPSED => 1; -use constant LASTTAG => 2; -use constant TAGS => 3; -use constant SKIP => 4; -use constant MINIMUM => 5; -use constant SKIPCOUNT => 6; -use constant CONFIDENCE => 7; -use constant ERROR => 8; -use constant STAT => 9; - -# ------------------------------------------------------------------------ -# Constructor - -=item $t = Benchmark::Timer->new( [options] ); - -Constructor for the Benchmark::Timer object; returns a reference to a -timer object. Takes the following named arguments: - -=over 4 - -=item skip - -The number of trials (if any) to skip before recording timing information. - -=item minimum - -The minimum number of trials to run. - -=item error - -A percentage between 0 and 100 which indicates how much error you are willing -to tolerate in the average time measured by the benchmark. For example, a -value of 1 means that you want the reported average time to be within 1% of -the real average time. C<need_more_samples()> will use this value to determine -when it is okay to stop collecting data. - -If you specify an error you must also specify a confidence. - -=item confidence - -A percentage between 0 and 100 which indicates how confident you want to be in -the error measured by the benchmark. For example, a value of 97.5 means that -you want to be 97.5% confident that the real average time is within the error -margin you have specified. C<need_more_samples()> will use this value to -compute the estimated error for the collected data, so that it can determine -when it is okay to stop. - -If you specify a confidence you must also specify an error. - -=back - -=cut - -sub new { - my $class = shift; - my $self = []; - bless $self, $class; - return $self->reset(@_); -} - - -# ------------------------------------------------------------------------ -# Public methods - -=item $t->reset; - -Reset the timer object to the pristine state it started in. -Erase all memory of tags and any previously accumulated timings. -Returns a reference to the timer object. It takes the same arguments -the constructor takes. - -=cut - -sub reset { - my $self = shift; - my %args = @_; - - $self->[BEFORE] = {}; # [ gettimeofday ] storage - $self->[ELAPSED] = {}; # elapsed fractional seconds - $self->[LASTTAG] = undef; # what the last tag was - $self->[TAGS] = []; # keep list of tags in order seen - $self->[SKIP] = 0; # how many samples to skip - $self->[MINIMUM] = 1; # the minimum number of trails to run - $self->[SKIPCOUNT] = {}; # trial skip storage - delete $self->[CONFIDENCE]; # confidence factor - delete $self->[ERROR]; # allowable error - delete $self->[STAT]; # stat objects for each tag - - if(exists $args{skip}) { - croak 'argument skip must be a non-negative integer' - unless defined $args{skip} - and $args{skip} !~ /\D/ - and int $args{skip} == $args{skip}; - $self->[SKIP] = $args{skip}; - delete $args{skip}; - } - - if(exists $args{minimum}) { - croak 'argument minimum must be a non-negative integer' - unless defined $args{minimum} - and $args{minimum} !~ /\D/ - and int $args{minimum} == $args{minimum}; - croak 'argument minimum must greater than or equal to skip' - unless defined $args{minimum} - and $args{minimum} >= $self->[SKIP]; - $self->[MINIMUM] = $args{minimum}; - delete $args{minimum}; - } - - my $confidence_is_valid = - (defined $args{confidence} - and $args{confidence} =~ /^\d*\.?\d*$/ - and $args{confidence} > 0 - and $args{confidence} < 100); - - my $error_is_valid = - (defined $args{error} - and $args{error} =~ /^\d*\.?\d*$/ - and $args{error} > 0 - and $args{error} < 100); - - if ($confidence_is_valid && !$error_is_valid || - !$confidence_is_valid && $error_is_valid) - { - carp 'you must specify both confidence and error' - } - elsif ($confidence_is_valid && $error_is_valid) - { - $self->[CONFIDENCE] = $args{confidence}; - delete $args{confidence}; - - $self->[ERROR] = $args{error}; - delete $args{error}; - - # Demand load the module we need. We could just - # require people to install it... - croak 'Could not load the Statistics::PointEstimation module' - unless eval "require Statistics::PointEstimation"; - } - - if(%args) { - carp 'skipping unknown arguments'; - } - - return $self; -} - - -=item $t->start($tag); - -Record the current time so that when C<stop()> is called, we can calculate an -elapsed time. - -=cut - -# In this routine we try hard to make the [ gettimeofday ] take place -# as late as possible to minimize Heisenberg problems. :) - -sub start { - my $self = shift; - my $tag = shift || $self->[LASTTAG] || '_default'; - $self->[LASTTAG] = $tag; - if(exists $self->[SKIPCOUNT]->{$tag}) { - if($self->[SKIPCOUNT]->{$tag} > 1) { - $self->[SKIPCOUNT]->{$tag}--; - } else { - $self->[SKIPCOUNT]->{$tag} = 0; - push @{$self->[BEFORE]->{$tag}}, [ gettimeofday ]; - } - } else { - push @{$self->[TAGS]}, $tag; - $self->[SKIPCOUNT]->{$tag} = $self->[SKIP] + 1; - if($self->[SKIPCOUNT]->{$tag} > 1) { - $self->[SKIPCOUNT]->{$tag}--; - } else { - $self->[SKIPCOUNT]->{$tag} = 0; - $self->[BEFORE]->{$tag} = [ [ gettimeofday ] ] - } - } -} - - -=item $t->stop($tag); - -Record timing information. If $tag is supplied, it must correspond to one -given to a previously called C<start()> call. It returns the elapsed time in -milliseconds. C<stop()> croaks if the timer gets out of sync (e.g. the number -of C<start()>s does not match the number of C<stop()>s.) - -=cut - -sub stop { - my $after = [ gettimeofday ]; # minimize overhead - my $self = shift; - my $tag = shift || $self->[LASTTAG] || '_default'; - - croak 'must call $t->start($tag) before $t->stop($tag)' - unless exists $self->[SKIPCOUNT]->{$tag}; - - return if $self->[SKIPCOUNT]->{$tag} > 0; - - my $i = exists $self->[ELAPSED]->{$tag} ? - scalar @{$self->[ELAPSED]->{$tag}} : 0; - my $before = $self->[BEFORE]->{$tag}->[$i]; - croak 'timer out of sync' unless defined $before; - - # Create a stats object if we need to - if (defined $self->[CONFIDENCE] && !defined $self->[STAT]->{$tag}) - { - $self->[STAT]->{$tag} = Statistics::PointEstimation->new; - $self->[STAT]->{$tag}->set_significance($self->[CONFIDENCE]); - } - - my $elapsed = tv_interval($before, $after); - - if($i > 0) { - push @{$self->[ELAPSED]->{$tag}}, $elapsed; - } else { - $self->[ELAPSED]->{$tag} = [ $elapsed ]; - } - - $self->[STAT]->{$tag}->add_data($elapsed) - if defined $self->[STAT]->{$tag}; - - return $elapsed; -} - - -=item $t->need_more_samples($tag); - -Compute the estimated error in the average of the data collected thus far, and -return true if that error exceeds the user-specified error. If a $tag is -supplied, it must correspond to one given to a previously called C<start()> -call. - -This routine assumes that the data are normally distributed. - -=cut - -sub need_more_samples { - my $self = shift; - my $tag = shift || $self->[LASTTAG] || '_default'; - - carp 'You must set the confidence and error in order to use need_more_samples' - unless defined $self->[CONFIDENCE]; - - # In case this function is called before any trials are run - return 1 - if !defined $self->[STAT]->{$tag} || - $self->[STAT]->{$tag}->count < $self->[MINIMUM]; - - # For debugging -# printf STDERR "Average: %.5f +/- %.5f, Samples: %d\n", -# $self->[STAT]->{$tag}->mean(), $self->[STAT]->{$tag}->delta(), -# $self->[STAT]->{$tag}->count; -# printf STDERR "Percent Error: %.5f > %.5f\n", -# $self->[STAT]->{$tag}->delta() / $self->[STAT]->{$tag}->mean() * 100, -# $self->[ERROR]; - - return (($self->[STAT]->{$tag}->delta() / $self->[STAT]->{$tag}->mean() * 100) > - $self->[ERROR]); -} - - -=item $t->report($tag); - -Returns a string containing a simple report on the collected timings for $tag. -This report contains the number of trials run, the total time taken, and, if -more than one trial was run, the average time needed to run one trial and -error information. C<report()> will complain (via a warning) if a tag is -still active. - -=cut - -sub report { - my $self = shift; - my $tag = shift || $self->[LASTTAG] || '_default'; - - unless(exists $self->[ELAPSED]->{$tag}) { - carp join ' ', 'tag', $tag, 'still running'; - return; - } - - return $self->_report($tag); -} - - -=item $t->reports; - -In a scalar context, returns a string containing a simple report on the -collected timings for all tags. The report is a concatenation of the -individual tag reports, in the original tag order. In an list context, returns -a hash keyed by tag and containing reports for each tag. The return value is -actually an array, so that the original tag order is preserved if you assign -to an array instead of a hash. C<reports()> will complain (via a warning) if a -tag is still active. - - -=cut - -sub reports { - my $self = shift; - - if (wantarray) - { - my @reports; - - foreach my $tag (@{$self->[TAGS]}) { - push @reports, $tag; - push @reports, $self->report($tag); - } - - return @reports; - } - else - { - my $report = ''; - - foreach my $tag (@{$self->[TAGS]}) { - $report .= $self->report($tag); - } - - return $report; - } -} - - -sub _report { - my $self = shift; - my $tag = shift; - - unless(exists $self->[ELAPSED]->{$tag}) { - return "Tag $tag is still running or has not completed its skipped runs, skipping\n"; - } - - my $report = ''; - - my @times = @{$self->[ELAPSED]->{$tag}}; - my $n = scalar @times; - my $total = 0; $total += $_ foreach @times; - - if ($n == 1) - { - $report .= sprintf "\%d trial of \%s (\%s total)\n", - $n, $tag, timestr($total); - } - else - { - $report .= sprintf "\%d trials of \%s (\%s total), \%s/trial\n", - $n, $tag, timestr($total), timestr($total / $n); - } - - if (defined $self->[STAT]->{$tag}) - { - my $delta = 0; - $delta = $self->[STAT]->{$tag}->delta() - if defined $self->[STAT]->{$tag}->delta(); - - $report .= sprintf "Error: +/- \%.5f with \%s confidence\n", - $delta, $self->[CONFIDENCE]; - } - - return $report; -} - - -=item $t->result($tag); - -Return the time it took for $tag to elapse, or the mean time it took for $tag -to elapse once, if $tag was used to time code more than once. C<result()> will -complain (via a warning) if a tag is still active. - -=cut - -sub result { - my $self = shift; - my $tag = shift || $self->[LASTTAG] || '_default'; - unless(exists $self->[ELAPSED]->{$tag}) { - carp join ' ', 'tag', $tag, 'still running'; - return; - } - my @times = @{$self->[ELAPSED]->{$tag}}; - my $total = 0; $total += $_ foreach @times; - return $total / @times; -} - - -=item $t->results; - -Returns the timing data as a hash keyed on tags where each value is -the time it took to run that code, or the average time it took, -if that code ran more than once. In scalar context it returns a reference -to that hash. The return value is actually an array, so that the original -tag order is preserved if you assign to an array instead of a hash. - -=cut - -sub results { - my $self = shift; - my @results; - foreach my $tag (@{$self->[TAGS]}) { - push @results, $tag; - push @results, $self->result($tag); - } - return wantarray ? @results : \@results; -} - - -=item $t->data($tag), $t->data; - -These methods are useful if you want to recover the full internal timing -data to roll your own reports. - -If called with a $tag, returns the raw timing data for that $tag as -an array (or a reference to an array if called in scalar context). This is -useful for feeding to something like the Statistics::Descriptive package. - -If called with no arguments, returns the raw timing data as a hash keyed -on tags, where the values of the hash are lists of timings for that -code. In scalar context, it returns a reference to that hash. As with -C<results()>, the data is internally represented as an array so you can -recover the original tag order by assigning to an array instead of a hash. - -=cut - -sub data { - my $self = shift; - my $tag = shift; - my @results; - if($tag) { - if(exists $self->[ELAPSED]->{$tag}) { - @results = @{$self->[ELAPSED]->{$tag}}; - } else { - @results = (); - } - } else { - @results = map { ( $_ => $self->[ELAPSED]->{$_} || [] ) } - @{$self->[TAGS]}; - } - return wantarray ? @results : \@results; -} - - -# ------------------------------------------------------------------------ -# Internal utility subroutines - -# timestr($sec) takes a floating-point number of seconds and formats -# it in a sensible way, commifying large numbers of seconds, and -# converting to milliseconds if it makes sense. Since Time::HiRes has -# at most microsecond resolution, no attempt is made to convert into -# anything below that. A unit string is appended to the number. - -sub timestr { - my $sec = shift; - my $retstr; - if($sec >= 1_000) { - $retstr = commify(int $sec) . 's'; - } elsif($sec >= 1) { - $retstr = sprintf $sec == int $sec ? '%ds' : '%0.3fs', $sec; - } elsif($sec >= 0.001) { - my $ms = $sec * 1_000; - $retstr = sprintf $ms == int $ms ? '%dms' : '%0.3fms', $ms; - } elsif($sec >= 0.000001) { - $retstr = sprintf '%dus', $sec * 1_000_000; - } else { - # I'll have whatever real-time OS she's having - $retstr = $sec . 's'; - } - $retstr; -} - - -# commify($num) inserts a grouping comma according to en-US standards -# for numbers larger than 1000. For example, the integer 123456 would -# be written 123,456. Any fractional part is left untouched. - -sub commify { - my $num = shift; - return unless $num =~ /\d/; - return $num if $num < 1_000; - - my $ip = int $num; - my($fp) = ($num =~ /\.(\d+)/); - - $ip =~ s/(\d\d\d)$/,$1/; - 1 while $ip =~ s/(\d)(\d\d\d),/$1,$2,/; - - return $fp ? join '.', $ip, $fp : $ip; -} - - -# ------------------------------------------------------------------------ -# Finish up the POD. - -=back - -=head1 BUGS - -Benchmarking is an inherently futile activity, fraught with uncertainty -not dissimilar to that experienced in quantum mechanics. But things are a -little better if you apply statistics. - -=head1 SEE ALSO - -L<Benchmark>, L<Time::HiRes>, L<Time::Stopwatch>, L<Statistics::Descriptive> - -=head1 AUTHOR - -The original code (written before April 20, 2001) was written by Andrew Ho -E<lt>andrew@zeuscat.comE<gt>, and is copyright (c) 2000-2001 Andrew Ho. -Versions up to 0.5 are distributed under the same terms as Perl. - -Maintenance of this module is now being done by David Coppit -E<lt>david@coppit.orgE<gt>. - -=cut - - -# ------------------------------------------------------------------------ -# Return true for a valid Perl include - -1; - - -# ======================================================================== -__END__ |
