Полезная информация

## 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;
\$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: \$!";

\$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````

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