Click here to Skip to main content
13,054,329 members (122,623 online)
Click here to Skip to main content
Add your own
alternative version


25 bookmarked
Posted 1 May 2003

CProfile - A simple class to do code profiling and tracing

, 1 May 2003
Rate this:
Please Sign up or sign in to vote.
This simple class allows to profile code sections and optionally print time elapsed and tracing strings in different formats.


I wrote the CProfiler class to continue my learning of the Win32 API and C++ programming. The basic idea was to implement a simple class to do code and function profiling functions, in a very easy manner. I searched through the Platform SDK and found the QueryPerformanceCounter and QueryPerformanceFrequency functions in the Win32 API. I started from here and I've finished with a raw, but functional class. It was fun to program (at least for me, a hobbyist and novice C++ programmer.)

This code was written (and tested only) in Visual C++ 7.0 (2002).

Using the code

The CProfiler class declaration is the following:

<PRE lang=mc++>class CProfiler { public: CProfiler(void); ~CProfiler(void); void ProfileStart(LOGTYPE logtype = LOGNONE); __int64 ProfileEnd (char* TraceStr = ""); double SecsFromTicks ( __int64 ticks); DWORD GetLastRetVal(void); private: LARGE_INTEGER m_QPFrequency; // ticks/sec resolution LARGE_INTEGER m_StartCounter; // start time LARGE_INTEGER m_EndCounter; // finish time __int64 m_ElapsedTime; // elapsed time DWORD m_Retval; // return value for API functions LOGTYPE m_LogType; // logging type };

Well, let's start, from looking at the class declaration, on how to use it.

First, of course, you need to create an object of type CProfiler. As you see above, the constructor does not take any parameters; it does the following initialization tasks: clears all LARGE_INTEGER unions with ZeroMemory API, and gets the high resolution counter frequency for the local machine through the QueryPerformanceFrequency API. This is stored in the m_QPFrequency data member.

The constructor, the ProfileStart function and the ProfileEnd function use the m_Retval private data member to store the return values from the APIs. You can check if you want, if the API has failed after the object construction, a call to ProfileStart or ProfileEnd using the member function GetLastRetVal.

After a successful object construction you can begin using the ProfileStart and ProfileEnd functions, which are PLAY/STOP buttons for the timer.

If you look at those functions they are defined as:

<PRE lang=mc++>void ProfileStart(LOGTYPE logtype = LOGNONE); // starts profiling __int64 ProfileEnd (char* TraceStr = ""); // end profiling

Observe that I've defined the ProfileStart function to take an argument of type LOGTYPE, which defaults to LOGNONE... What is this? Its an enumeration which I use for indicating what type of debug logging is used. The <CODE lang=mc++>enum is declared as follows:


With this enumeration, I'm assured that only those five values can be used as arguments.

Let's explain what the different logging types are for the ProfileStart function: LOGNONE does not log anything to anywhere, LOGTICKS logs the elapsed time in clock ticks; LOGSEGS logs the elapsed time in seconds; LOGALL logs the elapsed time in both formats; and finally LOGMSGBOX also displays the time in both formats, both displaying a dialog (exactly like the dialog in the top of this article...). Note that LOGTICKS, LOGSECS and LOGALL arguments write to the VC++ debugger results window, using the OutputDebugString function.

The general usage mode of the CProfiler class can be expressed in the following code:

<PRE lang=mc++>#include <iostream> #include "profiler.h" using namespace std; int main(void) { // create object CProfiler cprof; int i; float f; __int64 ptime; // start profiling (no debug output) cprof.ProfileStart(LOGNONE); for (i = 0, f = 0.0; i < 10000; i++, f+ = 0.5) cout<< static_cast<float>((i)*f/2) << endl; // end profile here and save the result ptime = cprof.ProfileEnd(); cout << "Function time: " << ptime << " ticks (" << cprof.SecsFromTicks(ptime) << " seconds)" << endl; system("pause"); return 0; }

In the above code, we are profiling a quite stupid and senseless function, for demonstration purpose only: a function that calculates (i)*f/2 where i goes from 0 to 10000, and f, which is a float, from 0.0 to 500.0. Here we are using no debug logging, as indicates the line expressing ProfileStart(LOGNONE).

Next, we see ptime = ProfileEnd(). This function ends the profiling and returns the elapsed time (clock ticks) in a 64-bit signed integer (<CODE lang=mc++>__int64). Last, we use this value to express the elapsed time in clock ticks (ptime), and also in seconds, which can be obtained using the SecsFromTicks member function and passing an <CODE lang=mc++>__int64 value expressing the elapsed ticks. This is SecsFromTicks(ptime), which returns a <CODE lang=mc++>double.


To trace where the code is executing, we can also output a string with the debug logging output, using a <CODE lang=mc++>char* parameter with the ProfileEnd member function. For example, you could write:

<PRE lang=mc++>float MyComplexCalc (float x, float y, float z) { ... // complex function code ;) return fResult; } // profile the MyComplexCalc function int main(void) { CProfile prof; ProfileStart (LOGTICKS); double fcalc = MyComplexCalc (7.0, 0.6, 8.2); ProfileEnd ("Mycomplex calc function finished..."); }

Look at the ProfileEnd function. Now the parameter of the function is a string to trace where you are in the code, what function profiling are you doing, or whatever message you want to output along with the debug output logging specified with the LogType parameter in the ProfileStart member function.

The above code will output to the results window of the VC++ debugger (or to a debugging utility like SysInternals DebugView).

Logging modes

The following screenshots will show you an example, taken from the above sample code, on what to expect for each logging mode. The screenshots are from the DebugView utility. Expect a similar result in the output window of the Visual C++ debugger.

  1. Profiling with the above code, but with LOGALL logging type:

  2. Now with LOGTICKS...

  3. Next, with LOGSECS...

  4. And finally, the fancy (useful?) LOGMSGBOX.


This class was fun to write, was very helpful to continue learning my VC++ techniques. Note that I'm not a professional programmer, actually, I'm studying Social Science at the University, but I like very much to develop software.

The purpose, in fact, of this little code is to receive criticism and recommendations on how to improve my coding style, to avoid errors, etc. If someone finds this CProfiler class useful (or as a base for a better class implementation) I would be very glad to hear comments.


  • 2, May, 2003 - V1.0 first release.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Hernán Di Pietro
Software Developer Nektra S.A
Argentina Argentina
Started the programming journey with the venerable Commodore 64, experimenting with 6510 ASM,BASIC and tapes. In 1990 I began playing with QuickBASIC, MSDOS, Win3.0 in a NECV20 XT clone, floppy-only system. With my first 386 system, I studied Turbo Pascal and C, and began exploring VGA graphics. Discovered Visual Basic under Windows 3.1. Moved to full C and C++ development in the 2000s, after Microsoft moved to .NET.

You may also be interested in...


Comments and Discussions

GeneralUpgraded CProfiler class [modified] Pin
Samuel Batista9-Oct-08 12:53
memberSamuel Batista9-Oct-08 12:53 
GeneralRe: Upgraded CProfiler class Pin
Hernán Di Pietro9-Oct-08 13:40
memberHernán Di Pietro9-Oct-08 13:40 
Generalexcellent! it's nice to start the profiling! Pin
nicekr22-Oct-06 20:20
membernicekr22-Oct-06 20:20 
GeneralA suggestion Pin
Anthony_Yio10-Sep-03 15:31
memberAnthony_Yio10-Sep-03 15:31 
GeneralAnother implementation Pin
c2j26-May-03 5:32
memberc2j26-May-03 5:32 
What is missing is an encapsulation class that issues the information in the destructor...

I append a small implementation (uses Watcom C++, so the ASM part needs to be translated) to show what I mean. It also uses my own String class and a debug logging function, trivial to be replaced by CString and OutputDebugString.

So now I use

void f(void)
// do something

void main(void) // sorry Smile | :)
clsTimer T("main()");



#ifndef _TIMERCLS_HPP_
#define _TIMERCLS_HPP_

class clsTimer
String _sDescr;
UINT32 _nStart;
UINT32 _nEnd;
clsTimer(LPCTSTR pszDescr)
_sDescr = pszDescr;
_nStart = GetTickCount();
_nEnd = GetTickCount();
_chk_utildebug(TEXT("%s: %d ms\n"),(LPCTSTR)_sDescr,_nEnd - _nStart);

class clsRDTSCTimer
String _sDescr;
UINT32 _nStart;
UINT32 _nEnd;
clsRDTSCTimer(LPCTSTR pszDescr)
_sDescr = pszDescr;
_nStart = RDTSC();
_nEnd = RDTSC();
_chk_utildebug(TEXT("%s: %d\n"),(LPCTSTR)_sDescr,_nEnd - _nStart);

#ifdef FINAL
#pragma message("warning: clsTimer included");

#endif // _TIMERCLS_HPP_

GeneralRe: Another implementation Pin
Electric Monk26-May-03 2:30
memberElectric Monk26-May-03 2:30 

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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170713.1 | Last Updated 2 May 2003
Article Copyright 2003 by Hernán Di Pietro
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid