High-Resolution Timers (Perl Cookbook, 2nd Edition)

## 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

Copyright © 2003 O'Reilly & Associates. All rights reserved.