Click here to Skip to main content
Click here to Skip to main content

Precision timing and the Principle of Least Action

, 6 Nov 2005
Rate this:
Please Sign up or sign in to vote.
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 Smile | :) .

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

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Jeremy Thornton

United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
Question[newbie] breaking lines Pinmemberjon_8016-Jan-09 9:24 
GeneralTimer Event PinmemberJeff C6-Sep-06 4:13 
GeneralNote: QueryPerformanceCounter() does not return the TSC PinmemberDon Clugston15-Nov-05 1:06 
QuestionRe: Note: QueryPerformanceCounter() does not return the TSC PinmemberJeremy Thornton15-Nov-05 5:35 
AnswerRe: Note: QueryPerformanceCounter() does not return the TSC PinmemberHal Angseesing16-Nov-05 7:08 
GeneralLess fluff please! Pinmemberyafan7-Nov-05 5:17 
GeneralRe: Less fluff please! PinmemberJeremy Thornton7-Nov-05 8:02 
GeneralRe: Less fluff please! PinmemberJake Loth15-Nov-05 2:49 
GeneralRe: Less fluff please! Pinmemberc2j217-Nov-05 3:32 
GeneralRe: Less fluff please! PinmemberDave Cross9-Jul-09 2:59 
GeneralRe: Less fluff please! PinmemberJake Loth9-Jul-09 6:39 
GeneralRe: Less fluff please! PinmemberJake Loth9-Jul-09 6:41 
GeneralRe: Less fluff please! Pinmemberyafan16-Dec-09 8:32 
GeneralFORMATTING PinmemberDavid Kemp7-Nov-05 4:23 
GeneralRe: FORMATTING PinmemberJeremy Thornton7-Nov-05 7:53 
AnswerRe: FORMATTING PinmemberGordon Brandly7-Nov-05 10:13 
GeneralRe: FORMATTING PinmemberJeremy Thornton7-Nov-05 11:20 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150128.1 | Last Updated 7 Nov 2005
Article Copyright 2005 by Jeremy Thornton
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid