Contents
Class: precision_timer
This is a C++ language compliant data type timer class capable of microsecond precision timing using the Time Stamp Counter present in all modern Intel processors, i.e. Pentium and later, accessed using the Windows API functions QueryPerformanceCounter(...)
and QueryPerformanceFrequency(...)
.
The class was written as an improvement to the 2001 CTimer[^] article by George Anescu by following the "Principle of Least Action" as applied to software. It offers an exception protected class with a more intuitive interface and mixed type arithmetic to help solve a variety of timing needs. The class is well documented using doxygen[^] compatible commands.
This class nicely demonstrates:
- the principle of least action applied to software design and implementation,
- microsecond timing techniques using the Time Stamp Counter (TSC),
- standard template library exceptions,
- clever techniques in operator overloading.
My M.Sc research generated the need to learn both C++ and to time a lot of stuff that happened very quickly. Developing a precision timing class that also behaved intuitively as a data type went a long way to solving both the problems :).
The principle of least action, formulated by Pierre Louis Maupertuis[^], who said that "Nature is thrifty in all its actions" is considered a 'deep' principle of Physics.
Applying this notion to software design leads to classes which behave in an intuitive and unsurprising way, in keeping with the underlying idioms of the language in which they are written.
Whilst applying least action to implementation results in the use of as many pre-existing software components as are necessary to produce the result without introducing too much complexity.
The result should be like the light taking the fastest path between two mediums, quick results and predictable behaviour.
Bjarne Stroustrup [^], designer and implementer of the C++[^] programming language, took a lot of effort to allow users to implement their own data types in terms of C++ programming idioms. Permitting intuitive class use and language extension through the technique of operator overloading is the key to applying these principles. However, a note of caution comes from Scott Meyers[^] in his book Effective C++ "Item 18: Strive for class interfaces that are complete and minimal". He says, only those operators that make sense to overload should be overloaded and in a way that is intuitive and in keeping with the behaviour of the in-built types.
The following code snippet is a contrived example demonstrating the usage:
#include "precision_timer.h"
...
precision_timer t1;
precision_timer t2;
if(ifstream file("results.dat")) {
file >> t1;
}
else {
...
}
t1.restart();
do{
... some timed activity
}
t1.stop();
t2.start();
do{
... some other timed activity
}
t2.stop();
t1 = t1 - t2;
t2 = 0;
t2.start();
do{
... some other timed activity
}
t2.stop();
cout << "Result = " << ((t1 / t2) / 1000) << "seconds";
if(ofstream file("results.dat")) {
file << t1;
}
else {
...
}
The TSC can readily be accessed through the Windows API using the QueryPerformanceCounter(...)[^]function. It can be used as a highly accurate timer when combined with the frequency of the TSC, retrieved using QueryPerformanceFrequency(...)[^].
For example, suppose that QueryPerformanceFrequency
indicates that the frequency of the high-resolution performance counter is 50000 counts per second. If the application calls QueryPerformanceCounter
immediately before and immediately after the section of the code to be timed, the counter values might be 1500 counts and 3500 counts, respectively. These values would indicate that .04 seconds (2000 counts / 50 000 sec-1) elapsed while the code was executed.
The first step in the process of constructing the precision_timer
class is to establish whether the processor has a TSC. The QueryPerformanceFrequency
function serves this purpose by returning a non-zero value if the installed hardware supports a time stamp counter. If not, as might be expected from a standpoint of least action, the class throws an exception and the class construction is aborted.
It seems that one of the less known features of the C++ Standard Template Library (STL) is the hierarchy of exceptions classes it offers:
- exception
bad_alloc
bad_cast
bad_typeid
logic_error
domain_error
invalid_argument
length_error
out_of_range
ios_base::failure
runtime_error
range_error
overflow_error
underflow_error
bad_exception
The precision_timer
class throws a runtime_error
with a suitable anodyne error message:
#include <stdexcept>
...
using namespace std;
...
if(!QueryPerformanceFrequency(&countsPerSecond) {
throw new runtime_exception("Fatal Error! The " +
"installed hardware does not support the " +
"high performance counter needed to use this class.");
}
else {
}
A class designed on the principle of least action seamlessly integrates into the C++ programming idiom of the languages built in types. The key to a class that integrates into the C++ programming idiom is operator overloading and the key to quick and easy operator overloading is implementing the binary operators in terms of the class' unary operators.
For example, operator+ for both precision_timer
and double types can be implemented in terms of operator+= and using the return value optimization, thus:
class precision_timer {
...
precision_timer& operator+=
(const precision_timer& right_arg);
...
};
const precision_timer operator+ (
const precision_timer& left_arg,
const precision_timer& right_arg,) {
return precision_timer(left_arg) += right_arg;
}
const precision_timer operator+ (
const precision_timer& left_arg,
const double right_arg) {
return precision_timer(left_arg) +=
precision_timer(right_arg);
}
Such an approach avoids the need for standalone binary operators to lever open the class encapsulation with the, so-called, 'friend
' keyword. Further, it means that only the unary assignment operators need to be maintained :).
Applying the pragmatic Principal of Least Action to both the design and implementation stages of computer programming can yield fast, maintainable results that are intuitive to use and predictable in their behaviour. I hope that some of the ideas presented in this article are interesting and possibly even useful. Feedback is, as always, very much appreciated.
- The C++ Programming Language 3rd edition - Bjarne Stroustrup
- More Effective C++ 35 New Ways to Improve Your Programs and Designs - Scott Meyers
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.