Click here to Skip to main content
15,886,799 members
Articles / Programming Languages / C++
Article

Precision timing and the Principle of Least Action

Rate me:
Please Sign up or sign in to vote.
3.60/5 (7 votes)
6 Nov 2005MIT5 min read 58.9K   486   24   17
An improved timer behaving like a built in type to explain a programming philosophy.

Contents

Introduction

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.

Background

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 applied to software design and implementation

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.

Example usage

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")) {
    //load the timer with the previously saved 
    //timing result in (fractions of) seconds
    file >> t1; 
}
else {
... 
}

t1.restart(); //continue from the previously saved time
do{
    ... some timed activity
}
t1.stop();

t2.start(); //start a fresh timer from zero
do{
    ... some  other timed activity
}
t2.stop();

t1 = t1 - t2; //subtract the second activity's time

t2 = 0; //reset to zero

t2.start();
do{
    ... some  other timed activity
}
t2.stop();

//display a calculated result

cout << "Result = " << ((t1 / t2) / 1000) << "seconds";

//save the results so far

if(ofstream file("results.dat")) {
    file << t1; //save the results so far
}
else {
... 
}

Implementation

Microsecond timing techniques using the Time Stamp Counter (TSC)

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.

Standard template library exceptions

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."); 
//exception on fail no TSC!
}
else {
    //carry on constructing the class
}

Clever techniques in operator overloading

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; 
//creating an un-named temporary makes it eligible 
//for return value optimisation by the compiler.
}
const precision_timer operator+ (
      const precision_timer& left_arg, 
               const double right_arg) {
    return precision_timer(left_arg) += 
                   precision_timer(right_arg); 
//construct with the passed double
}

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 :).

Conclusion

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.

References

  • The C++ Programming Language 3rd edition - Bjarne Stroustrup
  • More Effective C++ 35 New Ways to Improve Your Programs and Designs - Scott Meyers

Revision history

  • 15th October, 2005
    • Original article.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Question[newbie] breaking lines Pin
jon-8016-Jan-09 8:24
professionaljon-8016-Jan-09 8:24 
GeneralTimer Event Pin
Hooper266-Sep-06 3:13
Hooper266-Sep-06 3:13 
GeneralNote: QueryPerformanceCounter() does not return the TSC Pin
Don Clugston15-Nov-05 0:06
Don Clugston15-Nov-05 0:06 
QuestionRe: Note: QueryPerformanceCounter() does not return the TSC Pin
Jeremy Thornton15-Nov-05 4:35
Jeremy Thornton15-Nov-05 4:35 
AnswerRe: Note: QueryPerformanceCounter() does not return the TSC Pin
Hal Angseesing16-Nov-05 6:08
professionalHal Angseesing16-Nov-05 6:08 
GeneralLess fluff please! Pin
yafan7-Nov-05 4:17
yafan7-Nov-05 4:17 
GeneralRe: Less fluff please! Pin
Jeremy Thornton7-Nov-05 7:02
Jeremy Thornton7-Nov-05 7:02 
GeneralRe: Less fluff please! Pin
Jake Loth15-Nov-05 1:49
Jake Loth15-Nov-05 1:49 
GeneralRe: Less fluff please! Pin
c2j217-Nov-05 2:32
c2j217-Nov-05 2:32 
GeneralRe: Less fluff please! Pin
Dave Cross9-Jul-09 1:59
professionalDave Cross9-Jul-09 1:59 
GeneralRe: Less fluff please! Pin
Jake Loth9-Jul-09 5:39
Jake Loth9-Jul-09 5:39 
Of course!

Peace

GeneralRe: Less fluff please! Pin
Jake Loth9-Jul-09 5:41
Jake Loth9-Jul-09 5:41 
GeneralRe: Less fluff please! Pin
yafan16-Dec-09 7:32
yafan16-Dec-09 7:32 
GeneralFORMATTING Pin
David Kemp7-Nov-05 3:23
David Kemp7-Nov-05 3:23 
GeneralRe: FORMATTING Pin
Jeremy Thornton7-Nov-05 6:53
Jeremy Thornton7-Nov-05 6:53 
AnswerRe: FORMATTING Pin
Gordon Brandly7-Nov-05 9:13
Gordon Brandly7-Nov-05 9:13 
GeneralRe: FORMATTING Pin
Jeremy Thornton7-Nov-05 10:20
Jeremy Thornton7-Nov-05 10:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.