Ask the C++ Pro 10-Minute Solutions

Implementing a Stopwatch Class for Performance Measurements
By Danny Kalev

Commercial profiling suites are expensive and require some practice before you can use them effectively. This month, I will show how to implement a simple and effective stopwatch class that automatically calculates and reports the execution time of functions, loops, and code blocks.

Designing for Automation and Simplicity
The constructor and destructor of an automatic object execute at a block's beginning and end, respectively. We take advantage of this feature. The stopwatch's constructor starts counting time and its destructors calculate and report the total execution time of a certain operation. Profilers offer time resolution of a millisecond or less. To achieve a similar resolution, we will use the clock() function (declared in <time.h>). clock() returns the processor's elapsed time since the program's outset in clock ticks. A clock tick is a platform-dependent unit of time. The macro CLK_TCK represents the number of clock ticks per second on your machine.

Our stopwatch class looks like this:


#include <time.h>

class stopwatch
{
public:
 stopwatch() : start(clock()){} //start counting time
 ~stopwatch();
private:
 clock_t start;
};

The constructor initializes the member start with the current tick count. We don't define other member functions except for the destructor. The destructor calls clock() again, computes the time elapsed since the object's construction, and displays the results:


#include <iostream>
using namespace std;

stopwatch::~stopwatch()
{
 clock_t total = clock()-start; //get elapsed time
 cout<<"total of ticks for this activity: "<<total<<endl;
 cout< <"in seconds: "<< double(total/CLK_TCK) <<endl;
}
Note that clock_t and CLK_TCK are integers. Therefore, you have to cast them to double before a division. To delay the output on the screen, you can add the following lines to the destructor:


char dummy;
cin >>dummy; //delay output on the screen

You can also write the results to a file to log performance changes in different profiling sessions.

Measuring Performance With the Stopwatch Class
To measure the duration of a code block, create a local instance of the stopwatch class at the block's beginning. For example, suppose you want to measure the duration of the following loop that allocates 5000 string objects on the heap:


string *pstr[5000]; //array of pointers
for (int i=0;i<5000;i++)
{
 pstr[i] = new string;
}
Surround the relevant code in a pair of braces and create instantiate a stopwatch object at the block's beginning:

{
 stopwatch watch; // start measuring time
 string *pstr[5000];
 for (int i=0;i<5000;i++)
 {
  pstr[i] = new string;
 }
} // watch is destroyed here and reports the results

That's all! When the block begins, the watch starts counting time. When the block exits, the watch's destructor displays the results:


total of clock ticks for this activity: 27
in seconds: 0.027

The loop took 27 milliseconds on my machine. This result may seem impressive. However, what is the performance gain of replacing dynamic allocation with stack allocation? Let's try it and compare the results:


{
 stopwatch watch;
 for (int i=0;i<5000;i++)
 {
  string s;//create and destroy a local automatic string
 }
}

This time, the results are as follows:


total of clock ticks for this activity: 14
in seconds: 0.014

In other words, we achieved a 50% speed increase by using stack memory instead of heap memory. Considering that our heap version didn't count the time needed for destroying the 5000 strings—as opposed to the stack version—the results are even more impressive.

You probably noticed that our heap version also had 5000 assignment operations:


pstr[i] = new string;

The stack version didn't include an assignment expression. Could this skew the results? Again, let's try a slightly different form of the heap version:


 
{
 stopwatch watch;
 for (int i=0;i<5000;i++)
 {
  new string; // heap allocation without assignment
 }
}

Normally, you wouldn't write such code—it leaks memory abundantly. However, it isolates the allocation operation from other confounding variables. This is common practice in performance tuning. Here are the results of heap allocation without assignment:


total of clock ticks for this activity: 27
in seconds: 0.027
The assignment doesn't affect performance at all.

Performance measurements are tricky. Often, our intuition as developers is misleading—operations that we consider expensive incur no performance penalty at all, whereas seemingly innocuous operations such as dynamic memory allocation prove to be expensive in terms of CPU cycles. Without a reliable time measurement class such as stopwatch, we wouldn't have discovered these facts.

 
Other 10-Minute Solutions
 How to Change the Mouse Pointer without Flicker
 Setting Full Row Selection in ListView Control
 Automating Type Conversions with stringstream Objects
 Improving Memory Reallocation with Vectors
 How to Use <fstream> Classes for File I/O
 Casting About for Safe Typecasting
 Overloading Operator + the Right Way
 How to Create Persistent Objects
 Making Linked Lists More User-Friendly
 Preventing Glitches in Signal Processing
 Forcing Object Allocation on the Free-store
 Using String-Based Data Validation
 Implementing the 'Resource Acquisition Is Initialization' Idiom
 Simple Locks for Data Files
 Template Specializations
 Exception Handling
 Using Bit Fields in Data Optimization
 Using the Transform() Algorithm to Change a String's Case
 Use RTTI for Dynamic Type Identification
 Choosing the Right swap () Implementation
 Take Charge and Initialize Your Own Data
 Share Data Among Objects Using the Monostate Design Pattern
 String Manipulation Made Easy with std::string Algorithms
 Using typedef to Curb Miscreant Code
 Managing Objects' Construction Order
 Bitwise Operators: Combining Efficiency and Ease of Use
 Use Function Adapters to Extend Generic Algorithms' Usage
 Simplify Callback Dispatching with Enumerated Indexes
 Streamline Your Bulk I/O Operations with Stream Iterators
 Optimize Your Member Layout
 Preserve Code Safety with Conversion Operators
 Modify Your Base Class Interface in Derived Classes
 Tackle Common Programming Tasks Using the New <tuple> Library
 Use Local Classes for Proper Cleanup in Exception-enabled Apps
 Use multimap to Create Associative Containers with Duplicate Keys
 Enforcing Compile-time Constraints
 Facilitate Directory Operations with the <dirent.h> and <dir.h> Libraries
 Spruce Up Your Built-in Arrays
 Target 32- and 64-bit Platforms Together with a Few Simple Datatype Changes
 Restrict Object Allocation to Specific Memory Types
 Use the Pimpl Idiom to Reduce Compilation Time and Enhance Encapsulation
 Automate Resource Management with shared_ptr
 The Quick and Dirty Way to Add
 Pointing to Class Members
 Detecting Keystrokes While Your Application is Busy
 Linked Lists
 Programming the System Tray
 Create a "Universal" DLL
 Convert Path to Long Path Name
 Constructing an Object at a Pre-Determined Memory Position
 Declaring Classes and Member Functions in a Namespace
 Using the auto_ptr Class Template to Facilitate Dynamic Memory Management
 Using the random_shuffle() Algorithm to Randomize a Sequence of Elements
 Defining a Function Object
 Implementing the Singleton Design Pattern
 Declaring Function Pointers and Implementing Callbacks
 Overloading Operator << for a User-Defined Type
 Implementing a Stopwatch Class for Performance Measurements
 Creating and Accessing Environment Variables
 Executing an Object's Member Function in a Separate Thread
 Creating Heterogeneous Containers
 Overriding New and Delete
 Time and Date Manipulation
  Defining Functions with a Variable Argument List
 Optimize Abstract Operations with Function Templates




Sponsored Links


Advertising Info  |   Member Services  |   Contact Us  |   Help  |   Feedback  |   Site Map
Jupiterweb networks

internet.comearthweb.comDevx.comClickZ

Search Jupiterweb:

Jupitermedia Corporation has four divisions:
JupiterWeb, JupiterResearch, JupiterEvents, and JupiterImages

Copyright 2004 Jupitermedia Corporation All Rights Reserved.
Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

Jupitermedia Corporate Info | Newsletters | Tech Jobs | E-mail Offers