File Coverage

blib/lib/Benchmark/Stopwatch.pm
Criterion Covered Total %
statement 72 72 100.0
branch n/a
condition 3 6 50.0
subroutine 13 13 100.0
pod 7 8 87.5
total 95 99 95.9


line stmt bran cond sub pod time code
1 4     4   4327 use strict;
  4         9  
  4         169  
2 4     4   23 use warnings;
  4         11  
  4         267  
3              
4             package Benchmark::Stopwatch;
5              
6             our $VERSION = '0.05';
7              
8 4     4   5265 use Time::HiRes;
  4         9211  
  4         31  
9 4     4   5131 use Clone 'clone';
  4         16382  
  4         3199  
10              
11             =head1 NAME
12              
13             Benchmark::Stopwatch - simple timing of stages of your code.
14              
15             =head1 SYNOPSIS
16              
17             use Benchmark::Stopwatch;
18             my $stopwatch = Benchmark::Stopwatch->new->start;
19              
20             # ... code that reads from database ...
21             $stopwatch->lap('read from database');
22              
23             # ... code that writes to disk ...
24             $stopwatch->lap('write to disk');
25              
26             print $stopwatch->stop->summary;
27              
28             # NAME TIME CUMULATIVE PERCENTAGE
29             # read from database 0.123 0.123 34.462%
30             # write to disk 0.234 0.357 65.530%
31             # _stop_ 0.000 0.357 0.008%
32              
33             =head1 DESCRIPTION
34              
35             The other benchmark modules provide excellent timing for specific parts of
36             your code. This module aims to allow you to easily time the progression of
37             your code.
38              
39             The stopwatch analogy is that at some point you get a C stopwatch and
40             C timing. Then you note certain events using C. Finally you
41             C the watch and then print out a C.
42              
43             The summary shows all the events in order, what time they occured at, how long
44             since the last lap and the percentage of the total time. Hopefully this will
45             give you a good idea of where your code is spending most of its time.
46              
47             The times are all wallclock times in fractional seconds.
48              
49             That's it.
50              
51             =head1 METHODS
52              
53             =head2 new
54              
55             my $stopwatch = Benchmark::Stopwatch->new;
56            
57             Creates a new stopwatch.
58              
59             =cut
60              
61             sub new {
62 5     5 1 3581 my $class = shift;
63 5         14 my $self = {};
64              
65 5         97 $self->{events} = [];
66 5     4   26 $self->{_time} = sub { Time::HiRes::time() };
  4         15  
67              
68 5         22 return bless $self, $class;
69             }
70              
71             =head2 start
72              
73             $stopwatch = $stopwatch->start;
74              
75             Starts the stopwatch. Returns a reference to the stopwatch so that you can
76             chain.
77              
78             =cut
79              
80             sub start {
81 3     3 1 1679 my $self = shift;
82 3         13 $self->{start} = $self->time;
83 3         11 return $self;
84             }
85              
86             =head2 lap
87              
88             $stopwatch = $stopwatch->lap( 'name of event' );
89              
90             Notes down the time at which an event occurs. This event will later appear in
91             the summary.
92              
93             =cut
94              
95             sub lap {
96 3     3 1 14 my $self = shift;
97 3         5 my $name = shift;
98 3         8 my $time = $self->time;
99              
100 3         12 push @{ $self->{events} }, { name => $name, time => $time };
  3         14  
101 3         10 return $self;
102             }
103              
104             =head2 stop
105              
106             $stopwatch = $stopwatch->stop;
107              
108             Stops the stopwatch. Returns a reference to the stopwatch so you can chain.
109              
110             =cut
111              
112             sub stop {
113 2     2 1 77 my $self = shift;
114 2         8 $self->{stop} = $self->time;
115 2         9 return $self;
116             }
117              
118             =head2 total_time
119              
120             my $time_in_seconds = $stopwatch->total_time;
121              
122             Returns the time that the stopwatch ran for in fractional seconds. If the
123             stopwatch has not been stopped yet then it returns time it has been running
124             for.
125              
126             =cut
127              
128             sub total_time {
129 6     6 1 131 my $self = shift;
130              
131             # Get the stop time or now if missing.
132 6   66     21 my $stop = $self->{stop} || $self->time;
133              
134 6         15 return $stop - $self->{start};
135             }
136              
137             =head2 summary
138              
139             my $summary_text = $stopwatch->summary;
140              
141             Returns text summarizing the events that occured. Example output from a script
142             that fetches the homepages of the web's five busiest sites and times how long
143             each took.
144              
145             NAME TIME CUMULATIVE PERCENTAGE
146             http://www.yahoo.com/ 3.892 3.892 22.399%
147             http://www.google.com/ 3.259 7.152 18.758%
148             http://www.msn.com/ 8.412 15.564 48.411%
149             http://www.myspace.com/ 0.532 16.096 3.062%
150             http://www.ebay.com/ 1.281 17.377 7.370%
151             _stop_ 0.000 17.377 0.000%
152              
153             The final entry C<_stop_> is when the stop watch was stopped.
154              
155             =cut
156              
157             sub summary {
158 1     1 1 28 my $self = shift;
159 1         1 my $out = '';
160 1         1 my $header_format = "%-27.26s %-11s %-15s %s\n";
161 1         2 my $result_format = " %-27.26s %-11.3f %-15.3f %.3f%%\n";
162 1         2 my $prev_time = $self->{start};
163              
164 1         1 push @{ $self->{events} }, { name => '_stop_', time => $self->{stop} };
  1         5  
165              
166 1         9 $out .= sprintf $header_format, qw( NAME TIME CUMULATIVE PERCENTAGE);
167              
168 1         1 foreach my $event ( @{ $self->{events} } ) {
  1         3  
169              
170 4         5 my $duration = $event->{time} - $prev_time;
171 4         6 my $cumulative = $event->{time} - $self->{start};
172 4         7 my $percentage = ( $duration / $self->total_time ) * 100;
173              
174 4         39 $out .= sprintf $result_format, #
175             $event->{name}, #
176             $duration, #
177             $cumulative, #
178             $percentage;
179              
180 4         9 $prev_time = $event->{time};
181             }
182              
183 1         2 pop @{ $self->{events} };
  1         2  
184 1         7 return $out;
185             }
186              
187             =head2 as_data
188              
189             my $data_structure_hashref = $stopwatch->as_data;
190              
191             Returns a data structure that contains all the information that was logged.
192             This is so that you can use this module to gather the data but then use your
193             own code to manipulate it.
194              
195             The returned hashref will look like this:
196              
197             {
198             start_time => 1234500, # The time the stopwatch was started
199             stop_time => 1234510, # The time it was stopped or as_data called
200             total_time => 10, # The duration of timing
201             laps => [
202             {
203             name => 'test', # The name of the lap
204             time => 1, # The time of this lap (seconds)
205             cumulative => 1, # seconds since start to this lap
206             fraction => 0.10, # fraction of total time.
207             },
208             {
209             name => '_stop_', # created as needed
210             time => 9,
211             cumulative => 10,
212             fraction => 0.9,
213             },
214             ],
215             }
216              
217             =cut
218              
219             sub as_data {
220 1     1 1 2 my $self = shift;
221 1         2 my %data = ();
222              
223 1         3 $data{start_time} = $self->{start};
224 1   33     4 $data{stop_time} = $self->{stop} || $self->time;
225 1         2 $data{total_time} = $data{stop_time} - $data{start_time};
226              
227             # Clone the events across and add the stop event.
228 1         19 $data{laps} = clone( $self->{events} );
229 1         2 push @{ $data{laps} }, { name => '_stop_', time => $data{stop_time} };
  1         4  
230              
231             # For each entry in laps calculate the cumulative and the fraction.
232 1         2 my $running_total = 0;
233 1         1 my $last_time = $data{start_time};
234 1         2 foreach my $lap ( @{ $data{laps} } ) {
  1         2  
235              
236 3         5 my $this_time = delete $lap->{time};
237 3         13 $lap->{time} = $this_time - $last_time;
238 3         4 $last_time = $this_time;
239              
240 3         4 $running_total += $lap->{time};
241 3         23 $lap->{cumulative} = $running_total;
242 3         9 $lap->{fraction} = $lap->{time} / $data{total_time};
243             }
244              
245 1         19 return \%data;
246             }
247              
248             sub time {
249 11     11 0 1175 &{ $_[0]{_time} };
  11         45  
250             }
251              
252             =head1 AUTHOR
253              
254             Edmund von der Burg C<>
255              
256             L
257              
258             =head1 ACKNOWLEDGMENTS
259              
260             Inspiration from my colleagues at L
261              
262             =head1 COPYRIGHT
263              
264             Copyright (C) 2006 Edmund von der Burg. All rights reserved.
265              
266             This module is free software; you can redistribute it and/or modify it under
267             the same terms as Perl itself. If it breaks you get to keep both pieces.
268              
269             THERE IS NO WARRANTY.
270              
271             =cut
272              
273             1;