Timer class overview
Measuring time
The Timer class can be used to time events. By default,
a Timer class instance counts time in milliseconds since the first
Timer class instantiation of the program. Access the current time
with the getTime function which returns, by default, the
number of milliseconds since the timer was set. There are 1000
milliseconds in a second, so 1000 milliseconds equals one second.
Example 1 given below demonstrates how to access the current time.
The example code below will print the numbers from 0 to 10 as the
timer reaches 0, 1000, 2000, ... 10000 milliseconds.
Timer Example 1 |
full example |
|
stopwatch.reset(); // initialize timer to 0 millisec.
while (counter <= 10) {
if (stopwatch.getTime() > counter * 1000) {
cout << counter << " " << flush;
counter++;
}
}
|
Output: |
0 1 2 3 4 5 6 7 8 9 10
| |
The time unit can be set to any arbitrary indivisible unit with
the setTicksPerSecond function. Time units can be as small
as the CPU clock speed, but are intended to be about the length
of a millisecond or greater. For example, MIDI files have an
arbitrary time unit described as ticks per second in the header to
the file -- usually around 96 or so. A Timer object could match
the timing units in this case by calling setTicksPerSecond(96)
as shown in example 2 below.
Timer Example 2 |
full example |
|
stopwatch.reset();
stopwatch.setTicksPerSecond(96); // 96 time units per second
while (counter <= 10) {
if (stopwatch.getTime() > counter * 96) {
cout << counter << " " << flush;
counter++;
}
}
|
Output: |
0 1 2 3 4 5 6 7 8 9 10
| |
Warning: when changing the number of ticks per second with the
setTicksPerSecond function, you should call the reset function
at the same time. This will prevent a discontinuity which is created
in the tick count when the timing resolution is changed.
There is another function called getTimerInSeconds which
will return a floating-point value of the current time in seconds.
Likewise, a function called getTimeInTicks is an analogous
function which behaves in the exact same way as the getTime
function. A generic way to count seconds with an arbitrary number
of ticks per second is shown in example 3 below.
Timer Example 3 |
full example |
|
stopwatch.reset();
// set ticks rate between 100 and 999 ticks per second:
stopwatch.setTicksPerSecond(rand() % 900 + 100);
while (counter <= 10) {
if (stopwatch.getTime() >
counter*stopwatch.getTicksPerSecond()) {
cout << counter << " " << flush;
counter++;
}
}
|
Output: |
0 1 2 3 4 5 6 7 8 9 10
| |
Timing dependency on CPU speed
The Timer class must have the correct speed for the CPU in
order to give correct timing values. Since there is no direct way
(at least known to me) of getting the CPU speed of the computer in a
program, the Timer class will take a measurement of the CPU speed
when the very first instance of the Timer class is created by
calling the measureCpuSpeed function which takes about a
1/4 second to estimate the speed of the computer. The measured
value is expected to be about +/- 5% of the correct value, but
there is no guarentee to that since the measurement accuracy relies on
many variables and assumptions.
If you want an accurate timer, then you must set the exact
speed of your CPU with the setCpuSpeed function. For example,
the function call Timer::setCpuSpeed(200000000) sets the
CPU speed to 200 MHz. setCpuSpeed is a static function
which can be called through an instance of the Timer class, or it
can be called directly as shown in the previous sentence. Note
that changing the CPU speed for one instance of the Timer class
will change the value for all instances of the Timer class. You
may view the Timer classes value for the CPU speed by calling the
getCpuSpeed function. You can access the clock cycle count
directly with the function clockCycles which will return
the current number of clock cycles as a 64-bit integer since the
computer was last rebooted.
Warning: The Timer class is only guarenteed to work on
Pentium-class computers at the present time, since assembler code is used
to access the clock cycle count from a Pentium hardware register
in the function clockCycles. Assuming that the
clockCycles function returns the proper value, all of the other
class functions for the Timer class will work properly -- only the
clockCycles function is OS/CPU dependent.
Coordinating timers
All timers are coordinated with each other until the reset
function is called on one of them. Coordinated timers, when
running at the same number of ticks per second,
will output identical timing values. If you have two timers which
you want to reset, yet still produce the exact same output, you
would first reset one of the timers, then sync the two timers as shown
in example 4.
Timer Example 4: synchronizing two timers |
|
|
Timer timeA, timeB;
// timeA and timeB output identical values.
timeA.reset();
// timeA and timeB no longer output identical values
timeB.sync(timeA);
// timeA and timeB now output identical values again.
|
|
Although calling the setTicksPerSecond function on each
synchronized timer will cause them to output different numbers, the
two timers are in fact still synchronized and will output identical values
whenever they again have the same number of tick divisions per
second. You can also reset both timers at nearly the same instant,
but they would not be precisely coordinated with each other.
Periodic timing
The Timer class can also keep time in a cyclic manner which is often
useful for music-related timing. There are two ways to specify the
timing period of the timer:
- setPeriod -- this function will set the period
length in terms of ticks. By default, the period is 1000.0
ticks, which is equal to one second using the default timing
resolution of 1000 ticks per second. Note that the number of
ticks in the period can be a floating-point number. A
fractional position of into the tick time is allowable, since the
actual resolution of the timer is in CPU clock cycles.
- setTempo -- the input to this function is a musical
tempo marking. For example, if a quarter note equals 60
(MM = 60, which is one quarter note per second), then the period
could be set by calling setTempo(60). Note that this
function can also handle floating-point inputs.
The getPeriod function will return the current period duration
that the timer is set to, which by default is 1000.0.
Now that a period is specified, it is best to rest the timer
so that the start of the period is initialized. Once a periodic timer
is started, you can call either the expired or the
getPeriodCount functions to check on the period time:
- getPeriodCount -- returns the number of periods
which the timer was last updated or reset. The returned value is an
integer. For example, before one period duration has occured, the
return value will be 0, between one and two period durations, the
return value will be 1, and so on.
- expired -- behaves in a similar manner to
getPeriodCount but will return the period count as
a floating-point number. For example, half-way through the
first period's duration, the return value with be 0.5.
Period timers can be updated by advancing the inital period
pointer by an integral number of periods. This is useful to keep
track of events which need to run once every period. Without
updating a timer, the period count would continue to rise as
each new period duration is arrived at. You can update a
timer by two different methods, so that the period count cycles
from 0 to 1 (or any other number of cycles):
- reset -- once the first period (for example) is reached,
you could reset the timer, which would start counting periods
from zero again. This method is only good if you want to
have only an approximatly accurate period, since there will
be a gradual shift in the period's absolute position.
- update -- this method of updating the period start
position will exactly position the period counter at the
next period position, unlike the reset function which
will only approximately positon the period pointer by setting
it to the current time. update called without arguments
will update the period by one; giving an integer argument will
update the period pointer by the specified number of periods.
An example use of updating is a bar, or measure, of music: updating
at the beginning of each measure with result in a timer which keeps
track of the current beat in the measure (update(4) for a
4/4 measure).
Implementation notes
Notes from the webpage
http://www.sandpile.org/80x86/rdmsr.shtml:
The Time Stamp Counter resides (on Pentium and PentiumPro processors)
in the Model Specific Register #10h. It is incremented per each
internal processor clock cycle and provides therefore the most
accurate timing method in a PC. The counter is reset to zero after a
processor RESET, but not after an INIT. The Time Stamp Counter is 64
bit wide, which is enough to count more than 5,850 years, if a
processor runs at 100 MHz internal clock speed. After a TSC overflow
it restarts at zero. The CPUID instruction can be used to test for
an implemented Time Stamp Counter. The TSC value can be read, using
the RDTSC instruction (opcode 0Fh 31h),
So, an implementation in the C language to access the clock cycles
on a Pentuim CPU using a UNIX operating system would be:
unsigned long long int clockCount;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (clockCount));
The same result from a Microsoft-base operating system:
LONGLONG clockCount;
unsigned long high_end, low_end;
__asm {
__asm _emit 0x0f __asm _emit 0x31
mov high_end, edx
mov low_end, eax
}
clockCount = high_end;
clockCount = clockCount << 32;
clockCount |= low_end;