Recipe 3.9 High-Resolution Timers
3.9.1 Problem
You
need to measure time with a finer granularity than the full seconds
that time returns.
3.9.2 Solution
The Time::HiRes module, which is
included standard starting with the v5.8 release of Perl,
encapsulates this functionality for most systems:
use Time::HiRes qw(gettimeofday);
$t0 = gettimeofday( );
## do your operation here
$t1 = gettimeofday( );
$elapsed = $t1 - $t0;
# $elapsed is a floating point value, representing number
# of seconds between $t0 and $t1
3.9.3 Discussion
Here's some code that uses Time::HiRes to time how long the user
takes to press the Return key:
use Time::HiRes qw(gettimeofday);
print "Press return when ready: ";
$before = gettimeofday( );
$line = <STDIN>;
$elapsed = gettimeofday( ) - $before;
print "You took $elapsed seconds.\n";
Press return when ready:
You took 0.228149 seconds.
The module's
gettimeofday function returns a two-element list
representing seconds and microseconds when called in list context, or
a single floating-point number combining the two when called in
scalar context. You can also import its time
function to replace the standard core version by that name; this
always acts like scalar gettimeofday.
The module also
provides usleep and ualarm
functions, which are alternate versions of the standard Perl
sleep and alarm functions that
understand granularities of microseconds instead of just seconds.
They take arguments in microseconds; alternatively, you can import
the module's sleep and alarm
functions, which take floating-point arguments in seconds, to replace
the standard versions, which take integer arguments in seconds. For
access to your system's low-level itimer routines
(if you have them), setitimer and
getitimer are also
provided.
If your system doesn't support that module, you might try to poke
around by hand using syscall. Compare Time::HiRes
to the equivalent syscall code. (This example is
included principally so that you can see an example of Perl's
abstruse and archaic syscall function.)
require 'sys/syscall.ph';
# initialize the structures returned by gettimeofday
$TIMEVAL_T = "LL";
$done = $start = pack($TIMEVAL_T, (0,0));
# prompt
print "Press return when ready: ";
# read the time into $start
syscall(&SYS_gettimeofday, $start, 0) != -1
|| die "gettimeofday: $!";
# read a line
$line = <>;
# read the time into $done
syscall(&SYS_gettimeofday, $done, 0) != -1
|| die "gettimeofday: $!";
# expand the structure
@start = unpack($TIMEVAL_T, $start);
@done = unpack($TIMEVAL_T, $done);
# fix microseconds
for ($done[1], $start[1]) { $_ /= 1_000_000 }
# calculate time difference
$delta_time = sprintf "%.4f", ($done[0] + $done[1] )
-
($start[0] + $start[1] );
print "That took $delta_time seconds\n";
Press return when ready:
That took 0.3037 seconds
It's longer because it's doing system calls in Perl, whereas
Time::HiRes does them in C providing a single function. It's complex
because directly accessing system calls peculiar to your operating
system requires understanding the underlying C structures that the
system call takes and returns. Some programs that come with the Perl
distribution try to automatically calculate the formats to
pack and unpack for you, if fed
the appropriate C header file. In the example,
sys/syscall.ph is a Perl library file generated
with h2ph, which converts the
sys/syscall.h header file into
sys/syscall.ph, defining (among other things)
&SYS_gettimeofday as a subroutine that returns
the system call number of gettimeofday.
Here's another example of Time::HiRes, showing how you could use it
to benchmark a sort (if you didn't care to use the standard Benchmark
module):
use Time::HiRes qw(gettimeofday);
# take mean sorting time
$size = 2000;
$number_of_times = 100;
$total_time = 0;
for ($i = 0; $i < $number_of_times; $i++) {
my (@array, $j, $begin, $time);
# populate array
@array = ( );
for ($j=0; $j < $size; $j++) { push(@array, rand) }
# sort it
$begin = gettimeofday;
@array = sort { $a <=> $b } @array;
$time = gettimeofday-$begin;
$total_time += $time;
}
printf "On average, sorting %d random numbers takes %.5f seconds\n",
$size, ($total_time/$number_of_times);
On average, sorting 2000 random numbers takes 0.01033 seconds
3.9.4 See Also
The documentation for the Time::HiRes and Benchmark modules; the
syscall function in
perlfunc(1) and Chapter 29 of
Programming Perl; your system's
syscall(2) manpage
|