VC++: How to get elapsed time with ~1 MICROsecond precision

ArkM 0 Tallied Votes 174 Views Share

CPU Performance Counter based clock and stop-watch classes for Win2K++
were implemented in VC++. Details: see sources (I hope, these classes, especially StopWatch, have well-to-do interfaces;)).

Warning: no special precautions for multi-threading/multi-core CPU environment.

It's not so hard work to translate these codes in C (or other C++ implementations).

This codes are in the public domain.

/**
 * Performance Counter based clock for Win2K++.
 * VC++ declaration - version 2008-07-31.
 *
 * This code is in the public domain.
 * No warranties are made concerning
 * their correctness or stability,
 * and no user support is guaranteed.
 *
 * Be careful: it's elapsed (not CPU) time.
 * It's possible to get process time-slice
 * waiting intervals...
 */
class UltraClock
{
public:
	/// Activate UltraClock if it's supported.
	UltraClock();
	/// Get a time in seconds.
	double seconds() const
	{
		return ticks() / ticks_per_second;
	}
	/// Get a time in milliseconds.
	double millis() const 
	{ 
		return ticks() / ticks_per_millis;
	}
	/// Get a time in microseconds.
	double micros() const
	{
		return ticks() / ticks_per_micros;
	}
	/// Syntax shugar proxy for snap().
	double ticks() const { return snap(); }
	/// Is UltraClock supported?
	bool isActive() const { return active; }
	/// To throw away?..
	double operator()() const { return snap(); }
	// No need...
	//	__int64 int64ticks() const;
	// Query and get Performance Counter
	static double snap();
	/// Getters for internal adjusted constants.
	static double Ticks_per_second()
		{ return ticks_per_second; }
	static double Ticks_per_millis()
		{ return ticks_per_millis; }
	static double Ticks_per_micros()
		{ return ticks_per_micros; }
private:
	/// A constructor maiden flight?..
	static bool virgin;
	/// UltraClock is (not?) supported.
	static bool active;
	static double ticks_per_second;
	static double ticks_per_millis;
	static double ticks_per_micros;
};
// End of declarations (ultracloc.h).

/** 
 *  UltraClock implementation.
 *  Dependencies:
 *  - ultraclock.h (see above)
 *  - windows.h - obviously...
 *  - __int64 64-bit integers (not critical;)
 */
bool UltraClock::virgin = true;
bool UltraClock::active = false;
// Dynamically calculated "constants", don't worry:
double UltraClock::ticks_per_second = 1.0;
double UltraClock::ticks_per_millis = 1000.0;
double UltraClock::ticks_per_micros = 1000000.0;
namespace {
union Quad
{
	LARGE_INTEGER large;
	__int64 int64;
};
}
UltraClock::UltraClock()
{
    Quad q;
    if (virgin)
    {
        if (QueryPerformanceCounter(&q.large)
         && QueryPerformanceFrequency(&q.large))
        {
            UltraClock::virgin = false;
            if (q.int64)
            {
                ticks_per_second = static_cast<double>(q.int64);
                ticks_per_millis = ticks_per_second / 1000.0;
                ticks_per_micros = ticks_per_second / 1000000.0;
                UltraClock::active = true;
            }
        }
    }
}

namespace { UltraClock ultra; }

double UltraClock::snap()
{
	double counter = 0.0;
	if (UltraClock::active)
	{
		Quad q;
		if (QueryPerformanceCounter(&q.large))
		{
			counter = static_cast<double>(q.int64);
		}
	}
	return counter;
}

/**
 * UltraClock based stop-watch for Win2K++
 * with ~1.5 microseconds precision.
 */
class StopWatch
{
public:
	StopWatch():base(UltraClock::snap())
	{}
	double millis()
	{
		return (UltraClock::snap() - base) 
			/ UltraClock::Ticks_per_millis(); 
	}
	double millisBase() const 
	{ 
		return base / UltraClock::Ticks_per_millis(); 
	}
	double micros()
	{
		return (UltraClock::snap() - base) 
			/ UltraClock::Ticks_per_micros(); 
	}
	double microsBase() const 
	{ 
		return base / UltraClock::Ticks_per_micros(); 
	}
	double seconds()
	{
		return (UltraClock::snap() - base) 
			/ UltraClock::Ticks_per_second(); 
	}
	double secondsBase() const 
	{ 
		return base / UltraClock::Ticks_per_second(); 
	}
	double reset() 
	{ 
		double oldbase = base;
		base = UltraClock::snap();
		return oldbase;
	}
private:
	double base;
};
// End of UltraClock implementation file

// StopWatch example: 2D array versus vector<vector<...
const int M = 100;
const int N = 100;

namespace { double a[M][N]; }

void TestWatch()
{
    StopWatch ticker;
    double ta, tv;

    ticker.reset();
    for (int i = 0; i < M; ++i)
    {
        for (int j = 0; j < N; ++j)
            a[i][j] = i*N + j;
    }
    ta = ticker.micros();
    
    vector<vector<double> > v(M);
    
    ticker.reset();
    for (int i = 0; i < M; ++i)
    {
        v[i].reserve(N);
        for (int j = 0; j < N; ++j)
            v[i].push_back(a[i][j]);
    }
    tv = ticker.micros();
    cout <<
#ifdef _DEBUG
        "Debug"
#else
        "Release"
#endif
        " mode.\n"
        << "Initialization (in microseconds):\n"
        << "array[100][100]:\t" << ta 
        << ",\nvector<vector<> >:\t" << tv
        << endl;

    double suma, sumv;

    suma = 0.0;
    ticker.reset();
    for (int i = 0; i < M; ++i)
        for (int j = 0; j < N; ++j)
            suma += a[i][j];
    ta = ticker.micros();

    sumv = 0.0;
    ticker.reset();
    for (int i = 0; i < M; ++i)
        for (int j = 0; j < N; ++j)
            sumv += v[i][j];
    tv = ticker.micros();

    cout << "Sum calculation (" 
        << suma << "/" << sumv << "):\n"
        << "array:\t" << ta << ",\nvector:\t" << tv 
        << endl;
}
/* Output (from VC++ 2008):
Release mode.
Initialization (in microseconds):
array[100][100]:        57.2698,
vector<vector<> >:      131.86
Sum calculation (4.9995e+007/4.9995e+007):
array:  17.8794,
vector: 52.8
 -----------
Debug mode.
Initialization (in microseconds):
array[100][100]:        74.0318,
vector<vector<> >:      6598.6
Sum calculation (4.9995e+007/4.9995e+007):
array:  52.8,
vector: 1868.39
*/
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.