NAME
Timer::CPU - Precise user-space timer using the CPU clock
SYNOPSIS
use Timer::CPU;
my $elapsed_ticks = Timer::CPU::measure(sub {
## Do stuff
});
DESCRIPTION
For most timer operations, Time::HiRes is great. Since it provides
microsecond resolution in real "wall-clock" time it is very useful for
determining how long an operation actually takes in seconds.
However, on most CPUs it is possible to take much higher resolution
measurements. These measurements aren't in absolute units like seconds
but instead in "cycles" or "ticks". These measurements relate to
wall-clock time via an unspecified conversion ratio and can therefore
only be used for relative comparisons between code on the same machine
(and are subject to other constraints described below).
The resolution of this module is extremely high. For example, it can
detect the difference between a sub that returns nothing, one that
returns a number, and one that returns an empty string. It can detect
whether an "if" branch is taken or not, and even the difference between
"eq" comparing strings that match and strings that differ by only the
last character.
USAGE
This module provides one function: "Timer::CPU::measure". Its first
argument should be a callback code-ref. This is the code to be
benchmarked. It is always called in void context. The return value is a
number that corresponds to how many CPU cycles were spent executing your
callback.
The following other arguments can be passed in as keyword arguments:
"warm_ups", "iterations", and "method". For example:
say Timer::CPU::measure(sub { },
warm_ups => 10,
iterations => 50000,
method => 'median');
"measure" will first invoke your provided callback "warm_ups" times
(default is 2) and throw away the timing results. It will then invoke
your callback "iterations" times (default is 1000), recording the number
of ticks elapsed for each invocation. Finally, it will return a summary
of the results according to the "method" parameter which should be one
of "min", "max", "mean", or "median". The default method is "min".
CAVEATS
There are many caveats to this timing technique, but there are caveats
to all timing techniques.
It can be difficult to measure perl code by ticks elapsed because,
compared to C, perl code typically does a lot "under the hood".
On x86 and x86-64, this module uses the "rdtsc" ("ReaD Time-Stamp
Counter") instruction. On SPARC it accesses the %tick register which is
a 64-bit counter incremented every cycle. Various other CPUs might work
(see "cycle.h") although they haven't been tested. An architecture that
is noticeably missing is ARM.
As mentioned above, the real "wall-clock" time duration of a tick isn't
necessarily known. You may be able to figure out the clock frequency
with Sys::Info::Device::CPU, but (on x86/x86-64) you should first verify
your CPU is modernish and has a constant time-stamp counter (look for
"constant_tsc" in /proc/cpuinfo if you are on linux).
If your kernel context-switches out your process your timing data will
be corrupted. While performing benchmarks you should consider running in
single-user mode or, if you have multiple CPUs/cores, pegging your
process to a particular CPU (with something like Sys::CpuAffinity).
Many CPUs have the ability to dynamically scale their clock speed in
order to save power. If the CPU your process is running on changes clock
speed during your measurements your data will be corrupted. You should
consider fixing your CPU to a constant clock speed while running
benchmarks.
If the machine is hibernated or suspended, data will be corrupted also.
On some architectures, operating systems can disable access to the
time-stamp counter (ie by setting a bit in the CR4 register on x86).
This is uncommon but is sometimes done in virtualised environments to
protect against harvesting information from timing side-channels.
SEE ALSO
Timer-CPU github repo <https://github.com/hoytech/Timer-CPU>
Time::HiRes
Time-Stamp Counter <http://en.wikipedia.org/wiki/Time_Stamp_Counter>
String::Compare::ConstantTime
AUTHOR
Doug Hoyte, "<doug@hcsw.org>"
The tick collection routines are copied from the file "cycle.h" in the
FFTW 3 project. They are written by Matteo Frigo and contributors (see
source code) and are distributed under the MIT license.
COPYRIGHT & LICENSE
Copyright 2013-2014 Doug Hoyte.
This module is licensed under the same terms as perl itself.