Recipe 3.9. High-Resolution Timers
Author: Tom Christiansen & Nathan Torkington
Date: 1999-07-02T01:30:44Z

#

| Chapter 3 Dates and Times |

## 3.9. High-Resolution Timers

### Problem

You need to measure time with a finer granularity than the full seconds that `time` returns.

### Solution

This might not be possible. If your system supports both the `syscall` function in Perl as well as a system call like gettimeofday (2), then you could possibly use them to measure the time. The procedure for using `syscall` varies from system to system. The Discussion has sample code using it, but this is not necessarily portable.

The Time::HiRes module (available from CPAN) encapsulates this functionality for some 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

### Discussion

Here's some code that uses Time::HiRes to time how long the user takes to press RETURN:

    use Time::HiRes qw(gettimeofday);
    print "Press return when ready: ";
    $before = gettimeofday;
    $line = <>;
    $elapsed = gettimeofday-$before;
    print "You took $elapsed seconds.\n";
    Press return when ready:
    You took 0.228149 seconds.

Compare this to the equivalent `syscall` code:

    require 'sys/syscall.ph';

    # initialize the structures returned by gettimeofday
    $TIMEVAL_T = "LL";
    $done = $start = pack($TIMEVAL_T, ());

    # 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, while Time::HiRes does them in C providing a single function. It's complex because directly accessing system calls peculiar to your operating system requires you to know details about 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 that defines (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:

    use Time::HiRes qw(gettimeofday);
    # take mean sorting time
    $size = 500;
    $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 500 random numbers takes 0.02821 seconds

### See Also

The documentation for the CPAN modules Time::HiRes and Benchmark; the `syscall` function in perlfunc (1) and Chapter 3 of Programming Perl; your system's syscall (2) manpage

3.8. Printing a Date |  | 3.10. Short Sleeps