This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| High-performance Timing Library Submitted by

ltimer is yet another high-performance timing library. Timing libraries are a staple of realtime games, as they must perform important functions (poll for user input, render a new screen) in a timely fashion. Surely you've already got one. The difference is, this one is *clever*. Highlights of ltimer:

> - You can have as many timers as you like.
> - You can explicitly set the resolution of each timer. The default is milliseconds (1000 per second).
> - ltimer is self-calibrating. (Calibration currently takes two seconds. However, ltimer doesn't require a separate calibration step; it "hits the ground running".)
> - Once the ltimer library is calibrated, it's much faster than QueryPerformanceCounter(). Timings range from 3x to 6x faster, depending on your computer. (Before it's calibrated, it's about 1.1x slower than QueryPerformanceCounter().)
> - ltimer protects against what I call "retrograde time". (QueryPerformanceCounter() will, on rare occasions, report a time earlier than it did last time. For shame!)
> - ltimer is Win32-on-Intel only, and requires a Pentium processor or above.

The ltimer web page is here:
http://www.midwinter.com/~lch/programming/ltimer/ And you can download a fresh copy of the source code here:
http://www.midwinter.com/~lch/programming/ltimer/ltimer.zip

Currently browsing [ltimer.zip] (38,602 bytes) - [ltimer.cpp] - (12,479 bytes)

    /*
    ** [BEGIN NOTICE]
    **
    ** Copyright (C) 1999-2003 Larry Hastings
    **
    ** This software is provided 'as-is', without any express or implied warranty.
    ** In no event will the authors be held liable for any damages arising from
    ** the use of this software.
    **
    ** Permission is granted to anyone to use this software for any purpose,
    ** including commercial applications, and to alter it and redistribute
    ** it freely, subject to the following restrictions:
    **
    ** 1. The origin of this software must not be misrepresented; you must not
    **    claim that you wrote the original software. If you use this software
    **    in a product, an acknowledgment in the product documentation would be
    **    appreciated but is not required.
    ** 2. Altered source versions must be plainly marked as such, and must not be
    **    misrepresented as being the original software.
    ** 3. This notice may not be removed or altered from any source distribution.
    **
    ** The ltimer homepage is here:
    **		http://www.midwinter.com/~lch/programming/ltimer/
    **
    ** [END NOTICE]
    */

    /*
    ** ltimer
    **
    ** A machine-speed-independent timer class.
    **
    ** ltimer uses a two-tiered approach to determine the current time.
    ** For the first few seconds, it determines the time using
    ** QueryPerformanceCounter().  But after this calibration period is
    ** over, it can calculate what the local machine's clock frequency
    ** must be, and can use the Pentium-and-above RDTSC instruction.
    ** It then periodically recalculates the clock frequency--ltimer
    ** gets even more accurate over time.
    **
    ** For information on using RDTSC, peep this my homies:
    **   http://cedar.intel.com/software/idap/media/pdf/rdtscpm1.pdf
    ** or search the Intel developer site for "rdtsc cpuid timer"
    ** and wade through the results.
    **
    ** ltimer also prevents possible "retrograde" time observation.
    ** Every now and then I observe QueryPerformanceCounter() actually
    ** return a *smaller* value than the time before.  *shudder*
    ** This causes all sorts of conniptions in my timing loops.
    ** Anyway, ltimer prevents you from seeing this.  If QPC() ever
    ** reports a value smaller than the one it returned last time,
    ** ltimer ignores QPC() and uses the previous returned value.
    ** Your observed change in time will be 0, which I feel is an
    ** improvement over a negative number.  It doesn't bother with
    ** this for values returned by RDTSC, as I've never seen retrograde
    ** values with RDTSC.  (It seems far less likely, as RDTSC is
    ** returning the CPU's raw cycle count.)
    **
    ** (Warning: if your software actually travels back in time,
    ** either disable this feature or do not use ltimer.)
    */

    #define DEBUG_SLEEP_UNTIL 0

    #include <assert.h>
    #if DEBUG_SLEEP_UNTIL
    #include <stdio.h>
    #endif // DEBUG_SLEEP_UNTIL

    #include "ltimer.h"

    /*
    ** all of these macros assume the following
    **  *  there is a variable "returnValue" of some sort of integer/enum type
    **  *  there a goto label in the current function called "EXIT"
    **  *  zero is success, nonzero is failure
    */

    #define RETURN(expr)		\
    	{						\
    	returnValue = expr;		\
    	goto EXIT;				\
    	}						\

    /* if the expression "expr" is nonzero, return whatever "expr" was */
    #define ASSERT_SUCCESS(expr)\
    	{						\
    	returnValue = expr;		\
    	if (returnValue)		\
    	goto EXIT;			\
    	}						\

    /* if the expression "expr" is false, return rv */
    #define ASSERT_RETURN(expr, rv)	\
    	{						\
    	if (!(expr))			\
    		{					\
    		returnValue = rv;	\
    		goto EXIT;			\
    		}					\
    	}						\

    static ltimerUint64 globalRdtscStartTime;
    static ltimerUint64 globalQpcStartTime;

    static ltimerUint64 rdtscFrequency = 0;
    static ltimerUint64 qpcFrequency = 0;

    static ltimerUint64 useRdtscAfter;
    static ltimerUint64 recalibrateRdtscAfter;

    struct ltimerOption_s
    	{
    	ltimerUint32 calibrationPeriod;
    	ltimerUint32 recalibrationPeriod;
    	ltimerUint32 resolution;
    	ltimerUint32 mode;
    	ltimerUint32 longSleepThreshold;
    	ltimerUint32 shortSleepThreshold;
    	};

    static ltimerOption_s defaultOptions =
    	{
    	2000, // calibration period
    	2000, // recalibration period
    	1000, // resolution
    	LTIMER_TIMING_MODE_PERFORMANCE, // mode
    	4, // long sleep threshold
    	1, // short sleep threshold
    	};

    struct ltimer_s
    	{
    	ltimerUint64 rdtscCurrentTime;
    	ltimerUint64 qpcCurrentTime;
    	ltimerUint64 rdtscStartTime;
    	ltimerUint64 qpcStartTime;

    	ltimerUint64 lastReturnedTime;

    	ltimerUint32 useRdtsc;

    	ltimerOption_s options;

    #define CALCULATE_TIME(currentTime, name) \
    	(((currentTime - name ## StartTime) * options.resolution) / name ## Frequency); \

    #define RETURN_TIME(currentTime, name) \
    	{ \
    	lastReturnedTime = CALCULATE_TIME(currentTime, name); \
    	return lastReturnedTime; \
    	} \

    #define POLL_QPC() \
    	{ \
    	LARGE_INTEGER largeInteger; \
    	largeInteger.QuadPart = 0; \
    	QueryPerformanceCounter(&largeInteger); \
    	if ((ltimerUint64)largeInteger.QuadPart > qpcCurrentTime) \
    		qpcCurrentTime = largeInteger.QuadPart; \
    	} \

    #define CALIBRATE_RDTSC() \
    	{ \
    	rdtscFrequency = ((rdtscCurrentTime - globalRdtscStartTime) * qpcFrequency) / (qpcCurrentTime - globalQpcStartTime); \
    	recalibrateRdtscAfter = (rdtscCurrentTime + (ltimerUint64)((defaultOptions.recalibrationPeriod * rdtscFrequency) / 1000)); \
    	} \

    	// why is this a member function?
    	// 'cause it's actually faster this way.
    	// I guess MSVC is better at optimizing this pointers
    	// than pointers to structs that get used a lot.
    	// anyway, it means I'm "in context" as my friend Ryan
    	// used to say.  --lch
    	ltimerUint64 getCurrentTime(void)
    		{
    		if (useRdtsc)
    			{
    			rdtscCurrentTime = ltimerGetRDTSC();
    			if (rdtscCurrentTime <= recalibrateRdtscAfter)
    				RETURN_TIME(rdtscCurrentTime, rdtsc);
    			}

    		POLL_QPC();

    		if ((options.mode == LTIMER_TIMING_MODE_PERFORMANCE) && (qpcCurrentTime >= useRdtscAfter))
    			{
    			useRdtsc = 1;
    			rdtscCurrentTime = ltimerGetRDTSC();
    			CALIBRATE_RDTSC();
    			RETURN_TIME(rdtscCurrentTime, rdtsc);
    			}

    		RETURN_TIME(qpcCurrentTime, qpc);
    		}

    	ltimerUint64 getLastReportedTime(void)
    		{
    		return lastReturnedTime;
    		}

    #if DEBUG_SLEEP_UNTIL
    	ltimerUint32 longSleepCount;
    	ltimerUint32 shortSleepCount;
    #endif // DEBUG_SLEEP_UNTIL

    	HRESULT sleepUntil(ltimerUint64 wakeupTime)
    		{
    		// recalculate this on the fly--it can move around a little while we're running,
    		// due to recalibrating rdtscFrequency.
    		ltimerUint64 rdtscWakeupTime;
    		ltimerUint64 qpcWakeupTime = ((wakeupTime * qpcFrequency) / options.resolution) + qpcStartTime;

    #if DEBUG_SLEEP_UNTIL
    		longSleepCount = 0;
    		shortSleepCount = 0;

    		printf("sleepUntil(%6I64d): useRdtsc %d\n    ", wakeupTime, useRdtsc);
    #endif // DEBUG_SLEEP_UNTIL

    		for (;;)
    			{
    			getCurrentTime();

    			ltimerUint64 msTimeRemaining;
    			if (useRdtsc)
    				{
    				rdtscWakeupTime = ((wakeupTime * rdtscFrequency) / options.resolution) + rdtscStartTime;
    				if (rdtscCurrentTime >= rdtscWakeupTime)
    					break;
    				msTimeRemaining = ((rdtscWakeupTime - rdtscCurrentTime) * 1000) / rdtscFrequency;
    				}
    			else
    				{
    				if (qpcCurrentTime >= qpcWakeupTime)
    					break;
    				msTimeRemaining = ((qpcWakeupTime - qpcCurrentTime) * 1000) / qpcFrequency;
    				}

    			// stage one: if we have longSleepThreshold ms to wait,
    			// go ahead and sleep for longer.
    			if (msTimeRemaining >= options.longSleepThreshold)
    				{
    #if DEBUG_SLEEP_UNTIL
    				longSleepCount++;
    #endif // DEBUG_SLEEP_UNTIL
    				Sleep(options.longSleepThreshold >> 1);
    				continue;
    				}

    			// stage two: if we have more than shortSleepThreshold ms remaining,
    			// Sleep(0) (yield our timeslice).
    			if (msTimeRemaining >= options.shortSleepThreshold)
    				{
    #if DEBUG_SLEEP_UNTIL
    				shortSleepCount++;
    #endif // DEBUG_SLEEP_UNTIL
    				Sleep(0);
    				continue;
    				}
    			}

    #if DEBUG_SLEEP_UNTIL
    		printf("overshot %I64dms  Sleep(%d) x%4d  Sleep(0) x%4d  useRdtsc %d\n", getCurrentTime() - wakeupTime, options.longSleepThreshold, longSleepCount, shortSleepCount, useRdtsc);
    #endif // DEBUG_SLEEP_UNTIL

    		return S_OK;
    		}
    	};

    HRESULT LTIMER_FUNCTION ltimerStartup(void)
    	{
    	LARGE_INTEGER largeInteger;

    	QueryPerformanceCounter(&largeInteger);
    	globalRdtscStartTime = ltimerGetRDTSC();
    	globalQpcStartTime = largeInteger.QuadPart;

    	QueryPerformanceFrequency(&largeInteger);
    	qpcFrequency = largeInteger.QuadPart;

    	timeBeginPeriod(1);

    	// this ensures that some code in ltimerSetDefaultOption() is called,
    	// without actually changing the calibration period.
    	ltimerSetDefaultOption(LTIMER_OPTION_CALIBRATION_PERIOD, defaultOptions.calibrationPeriod);

    	// only allow performance mode for systems with one processor.
    	// note that it'll fail this test if dwNumberOfProcessors == 0;
    	// this is intentional, as it means the GetSystemInfo() call failed.
    	// A frightening eventuality to contemplate, but there you are.  --lch
    	SYSTEM_INFO systemInfo;
    	memset(&systemInfo, 0, sizeof(systemInfo));
    	GetSystemInfo(&systemInfo);
    	ltimerSetDefaultOption(LTIMER_OPTION_TIMING_MODE,
    		((systemInfo.dwNumberOfProcessors) == 1)
    		? LTIMER_TIMING_MODE_PERFORMANCE
    		: LTIMER_TIMING_MODE_SAFETY
    		);

    	return S_OK;
    	}

    HRESULT LTIMER_FUNCTION ltimerShutdown(void)
    	{
    	timeEndPeriod(1);
    	return S_OK;
    	}

    HRESULT LTIMER_FUNCTION ltimerSetDefaultOption(ltimerUint32 option, ltimerUint32 value)
    	{
    	if ((option == LTIMER_OPTION_INVALID) || (option > LTIMER_OPTION_LAST))
    		return ERROR_INVALID_ACCESS;

    	ltimerUint32 *optionArray = (ltimerUint32 *)&defaultOptions;
    	optionArray[option - 1] = value;

    	if (option == LTIMER_OPTION_CALIBRATION_PERIOD)
    		{
    		useRdtscAfter = globalQpcStartTime + (ltimerUint64)((defaultOptions.calibrationPeriod * qpcFrequency) / 1000);
    		}

    	return S_OK;
    	}

    HRESULT LTIMER_FUNCTION ltimerGetDefaultOption(ltimerUint32 option, ltimerUint32 *value)
    	{
    	if ((option == LTIMER_OPTION_INVALID) || (option > LTIMER_OPTION_LAST))
    		return ERROR_INVALID_ACCESS;

    	ltimerUint32 *optionArray = (ltimerUint32 *)&defaultOptions;
    	*value = optionArray[option - 1];
    	return S_OK;
    	}

    HRESULT LTIMER_FUNCTION ltimerSetOption(ltimer_t ltimer, ltimerUint32 option, ltimerUint32 value)
    	{
    	if ((option <= LTIMER_OPTION_LAST_GLOBAL_ONLY) || (option > LTIMER_OPTION_LAST))
    		return ERROR_INVALID_ACCESS;

    	ltimerUint32 *optionArray = (ltimerUint32 *)&(ltimer->options);
    	optionArray[option - 1] = value;

    	return S_OK;
    	}

    HRESULT LTIMER_FUNCTION ltimerGetOption(ltimer_t ltimer, ltimerUint32 option, ltimerUint32 *value)
    	{
    	if ((option <= LTIMER_OPTION_LAST_GLOBAL_ONLY) || (option > LTIMER_OPTION_LAST))
    		return ERROR_INVALID_ACCESS;

    	ltimerUint32 *optionArray = (ltimerUint32 *)&(ltimer->options);
    	*value = optionArray[option - 1];
    	return S_OK;
    	}

    HRESULT LTIMER_FUNCTION ltimerCreate(ltimer_t *ltimer_out)
    	{
    	HRESULT returnValue;
    	ltimer_t ltimer;

    	/* you *must* call ltimerStartup().  if you haven't, no soup for you! */
    	ASSERT_RETURN(qpcFrequency != 0, ERROR_NOT_READY);

    	ltimer = (ltimer_t)calloc(sizeof(ltimer_s), 1);
    	ASSERT_RETURN(ltimer != NULL, ERROR_OUTOFMEMORY);

    	ltimer->useRdtsc = 0;
    	memcpy(&(ltimer->options), &defaultOptions, sizeof(defaultOptions));

    	ASSERT_SUCCESS(ltimerSetOption(ltimer, LTIMER_OPTION_TIMING_MODE, defaultOptions.mode));
    	ASSERT_SUCCESS(ltimerReset(ltimer));

    EXIT:
    	*ltimer_out = ltimer;
    	return returnValue;
    	}

    HRESULT LTIMER_FUNCTION ltimerDestroy(ltimer_t *ltimer_out)
    	{
    	ltimer_t ltimer = *ltimer_out;
    	*ltimer_out = NULL;
    	free(ltimer);
    	return S_OK;
    	}

    HRESULT LTIMER_FUNCTION ltimerReset(ltimer_t ltimer)
    	{
    	LARGE_INTEGER largeInteger;
    	QueryPerformanceCounter(&largeInteger);
    	ltimer->rdtscStartTime = ltimerGetRDTSC();
    	ltimer->qpcStartTime = largeInteger.QuadPart;
    	return S_OK;
    	}

    ltimerUint64 LTIMER_FUNCTION ltimerGetCurrentTime(ltimer_t ltimer)
    	{
    	return ltimer->getCurrentTime();
    	}

    ltimerUint64 LTIMER_FUNCTION ltimerGetLastReportedTime(ltimer_t ltimer)
    	{
    	return ltimer->getLastReportedTime();
    	}

    HRESULT LTIMER_FUNCTION ltimerSleepUntil(ltimer_t ltimer, ltimerUint64 wakeupTime)
    	{
    	return ltimer->sleepUntil(wakeupTime);
    	}

    HRESULT LTIMER_FUNCTION ltimerSleep(ltimer_t ltimer, ltimerUint64 ticks)
    	{
    	return ltimerSleepUntil(ltimer, ltimer->getCurrentTime() + ticks);
    	}

Currently browsing [ltimer.zip] (38,602 bytes) - [main.cpp] - (6,466 bytes)

    /*
    ** [BEGIN NOTICE]
    **
    ** Copyright (C) 1999-2003 Larry Hastings
    **
    ** This software is provided 'as-is', without any express or implied warranty.
    ** In no event will the authors be held liable for any damages arising from
    ** the use of this software.
    **
    ** Permission is granted to anyone to use this software for any purpose,
    ** including commercial applications, and to alter it and redistribute
    ** it freely, subject to the following restrictions:
    **
    ** 1. The origin of this software must not be misrepresented; you must not
    **    claim that you wrote the original software. If you use this software
    **    in a product, an acknowledgment in the product documentation would be
    **    appreciated but is not required.
    ** 2. Altered source versions must be plainly marked as such, and must not be
    **    misrepresented as being the original software.
    ** 3. This notice may not be removed or altered from any source distribution.
    **
    ** The ltimer homepage is here:
    **		http://www.midwinter.com/~lch/programming/ltimer/
    **
    ** [END NOTICE]
    */

    #include <assert.h>
    #include <stdio.h>

    #include "ltimer.h"

    ltimerUint64 startTime;
    ltimer_t timer;
    ltimer_t sleeper;
    ltimerUint64 totalSleepTime = 0;

    // if you're using MSVC 6 and don't have the current Processor Pack installed,
    // you can't convert directly from an "unsigned __int64" to a "double".
    // Don't ask *me* why; ask the brainy eggheads at Microsoft.  --lch
    #define CAST_TO_DOUBLE(uint64) ((double)(__int64)(uint64))

    void sleepSomeMore(ltimerUint32 time)
    	{
    	totalSleepTime += time;
    	ltimerSleep(sleeper, time);
    	}

    void checkAfterSleeping(ltimerUint32 delta)
    	{
    	sleepSomeMore(delta);
    	ltimerUint64 endTime = ltimerGetCurrentTime(timer);
    	int deltaTime = (int)((__int64)endTime - (startTime + totalSleepTime));
    	assert((deltaTime < 100) && (deltaTime > -100));
    	}

    void smokeTest(void)
    	{
    	ltimerCreate(&timer);
    	startTime = ltimerGetCurrentTime(timer);

    	checkAfterSleeping(1000);
    	checkAfterSleeping(2500);
    	checkAfterSleeping(1500);

    	ltimerDestroy(&timer);
    	}

    static ltimerUint64 performanceFrequency = 0;
    static ltimerUint64 handicapQPC = 0;
    static ltimerUint64 handicapRDTSC = 0;

    class simpleTimer
    	{
    	public:

    		char description[256];
    		LARGE_INTEGER startTimeQPC;
    		ltimerUint64 startTimeRDTSC;
    		bool finished;
    		ltimerUint64 elapsedQPC;
    		ltimerUint64 elapsedRDTSC;

    		simpleTimer(char *format, ...)
    			{
    			va_list list;
    			va_start(list, format);
    			vsprintf(description, format, list);
    			va_end(list);

    			finished = false;
    			QueryPerformanceCounter(&startTimeQPC);
    			startTimeRDTSC = ltimerGetRDTSC();
    			}

    		void finish()
    			{
    			LARGE_INTEGER endTimeQPC;
    			QueryPerformanceCounter(&endTimeQPC);
    			ltimerUint64 endTimeRDTSC = ltimerGetRDTSC();

    			if (finished)
    				return;
    			finished = true;

    			elapsedQPC = endTimeQPC.QuadPart - (startTimeQPC.QuadPart + handicapQPC);
    			elapsedRDTSC = endTimeRDTSC - (startTimeRDTSC + handicapRDTSC);
    			printf("%18s: took %f seconds (%I64d machine cycles)\n", description, CAST_TO_DOUBLE(elapsedQPC) / CAST_TO_DOUBLE(performanceFrequency), endTimeRDTSC - startTimeRDTSC);
    			}

    		~simpleTimer()
    			{
    			finish();
    			}
    	};

    extern "C" void __fastcall doNothing(void)
    	{
    	}

    void timeNothing(ltimerUint32 iterations)
    	{
    	simpleTimer timer("nothing x %d", iterations);

    	// calling ltimerShutdown() is harmless, and it forces
    	// the compiler to generate code for the null loop.
    	for (ltimerUint32 i = iterations; i > 0; i--)
    		doNothing();
    	timer.finish();
    	handicapQPC = timer.elapsedQPC;
    	handicapRDTSC = timer.elapsedRDTSC;
    	}

    ltimerUint64 timeLtimer(ltimerUint32 iterations, ltimerUint32 mode)
    	{
    	ltimer_t ltimer;
    	ltimerCreate(<imer);
    	ltimerSetOption(ltimer, LTIMER_OPTION_TIMING_MODE, mode);

    	ltimerUint64 result;

    	simpleTimer timer("ltimer x %d", iterations);
    	for (ltimerUint32 i = iterations; i > 0; i--)
    		result = ltimerGetCurrentTime(ltimer);

    	timer.finish();

    	ltimerDestroy(<imer);

    	return timer.elapsedRDTSC;
    	}

    ltimerUint64 timeTGT(ltimerUint32 iterations)
    	{
    	simpleTimer timer("TGT x %d", iterations);

    	ltimerUint32 result;
    	for (ltimerUint32 i = iterations; i > 0; i--)
    		result = timeGetTime();
    	timer.finish();

    	return timer.elapsedRDTSC;
    	}

    ltimerUint64 timeQPC(ltimerUint32 iterations)
    	{
    	simpleTimer timer("QPC x %d", iterations);

    	LARGE_INTEGER result;
    	for (ltimerUint32 i = iterations; i > 0; i--)
    		QueryPerformanceCounter(&result);
    	timer.finish();

    	return timer.elapsedRDTSC;
    	}

    ltimerUint64 timeRDTSC(ltimerUint32 iterations)
    	{
    	simpleTimer timer("RDTSC x %d", iterations);

    	ltimerUint64 result;
    	for (ltimerUint32 i = iterations; i > 0; i--)
    		result = ltimerGetRDTSC();
    	timer.finish();

    	return timer.elapsedRDTSC;
    	}

    void performanceTest(char *argv1)
    	{
    	LARGE_INTEGER largeInteger;
    	QueryPerformanceFrequency(&largeInteger);
    	performanceFrequency = largeInteger.QuadPart;
    	printf("QPC's resolution is %I64d cycles per second.\n", performanceFrequency);

    	ltimerUint32 iterations = 1000 * 1000;
    	if (argv1 != NULL)
    		iterations = strtoul(argv1, NULL, 10);

    	timeNothing(iterations);
    	printf("From now on, we'll subtract that handicap.\n");
    	printf("\n");

    	printf("Forcing ltimer to use its slow method...\n");
    	ltimerUint64 ltimer1 = timeLtimer(iterations, LTIMER_TIMING_MODE_SAFETY);
    	ltimerUint64 tgt = timeTGT(iterations);
    	ltimerUint64 qpc = timeQPC(iterations);
    	ltimerUint64 rdtsc = timeRDTSC(iterations);

    	printf("\n");
    	printf("Sleeping to give ltimer time to calibrate, then using its fast method...\n");
    	ltimerUint32 sleepMs;
    	ltimerGetDefaultOption(LTIMER_OPTION_CALIBRATION_PERIOD, &sleepMs);
    	ltimerSleep(sleeper, sleepMs);
    	ltimerUint64 ltimer2 = timeLtimer(iterations, LTIMER_TIMING_MODE_PERFORMANCE);

    	printf("\n");
    	printf("When it hasn't warmed up yet, ltimer is %1.3fx slower than QPC.\n", CAST_TO_DOUBLE(ltimer1) / CAST_TO_DOUBLE(qpc));
    	printf("But once it's warmed up, ltimer is %1.3fx faster than QPC!\n", CAST_TO_DOUBLE(qpc) / CAST_TO_DOUBLE(ltimer2));
    	}

    int main(int argc, char *argv[])
    	{
    	ltimerStartup();
    	ltimerCreate(&sleeper);

    	smokeTest();

    	char *argv1 = (argc > 1) ? argv[1] : NULL;
    	performanceTest(argv1);

    	ltimerDestroy(&sleeper);

    	ltimerShutdown();

    	return 0;
    	}

Currently browsing [ltimer.zip] (38,602 bytes) - [ltimer.h] - (5,282 bytes)

    /*
    ** [BEGIN NOTICE]
    **
    ** Copyright (C) 1999-2003 Larry Hastings
    **
    ** This software is provided 'as-is', without any express or implied warranty.
    ** In no event will the authors be held liable for any damages arising from
    ** the use of this software.
    **
    ** Permission is granted to anyone to use this software for any purpose,
    ** including commercial applications, and to alter it and redistribute
    ** it freely, subject to the following restrictions:
    **
    ** 1. The origin of this software must not be misrepresented; you must not
    **    claim that you wrote the original software. If you use this software
    **    in a product, an acknowledgment in the product documentation would be
    **    appreciated but is not required.
    ** 2. Altered source versions must be plainly marked as such, and must not be
    **    misrepresented as being the original software.
    ** 3. This notice may not be removed or altered from any source distribution.
    **
    ** The ltimer homepage is here:
    **		http://www.midwinter.com/~lch/programming/ltimer/
    **
    ** [END NOTICE]
    */

    /*
    ** ltimer
    **
    ** A machine-speed-independent timer class.
    **
    ** ltimer uses a two-tiered approach to determine the current time.
    ** For the first LTIMER_SECONDS_UNTIL_USE_RDTSC seconds, it determines
    ** the time using QueryPerformanceCounter().  After that, it calculates
    ** what the local machine's clock frequency must be, and uses the
    ** Pentium-and-above RDTSC instruction.  It then recalculates the
    ** clock frequency after every LTIMER_RESYNC_RDTSC_FREQUENCY_EVERY calls,
    ** so it gets even more accurate over time.
    **
    ** For information on using RDTSC, peep this my homies:
    **   http://cedar.intel.com/software/idap/media/pdf/rdtscpm1.pdf
    ** or search the Intel developer site for "rdtsc cpuid timer"
    ** and wade through the results.
    **
    ** ltimer also prevents possible "retrograde" time observation.
    ** Every now and then I observe QueryPerformanceCounter() actually
    ** return a *smaller* value than the time before.  *shudder*
    ** This causes all sorts of conniptions in my timing loops.
    ** Anyway, ltimer prevents you from seeing this.  If QPC() ever
    ** reports a value smaller than the one it returned last time,
    ** ltimer ignores QPC() and uses the previous returned value.
    ** Your observed change in time will be 0, which I feel is an
    ** improvement over a negative number.  It doesn't bother with
    ** this for values returned by RDTSC, as I've never seen retrograde
    ** values with RDTSC.  (It seems far less likely, as RDTSC is
    ** returning the CPU's raw cycle count.)
    **
    ** (Warning: if your software actually travels back in time,
    ** either disable this feature or do not use ltimer.)
    **
    */

    #ifndef __LTIMER_H
    #define __LTIMER_H

    #ifdef __cplusplus
    extern "C" {
    #endif /* __cplusplus */

    #include <windows.h>

    struct ltimer_s;
    typedef struct ltimer_s ltimer_s;
    typedef ltimer_s *ltimer_t;

    #define LTIMER_FUNCTION  __fastcall

    typedef unsigned __int64 ltimerUint64;
    typedef unsigned     int ltimerUint32;

    extern      HRESULT LTIMER_FUNCTION ltimerStartup(void);
    extern      HRESULT LTIMER_FUNCTION ltimerShutdown(void);

    extern      HRESULT LTIMER_FUNCTION ltimerGetDefaultOption(ltimerUint32 option, ltimerUint32 *value);
    extern      HRESULT LTIMER_FUNCTION ltimerSetDefaultOption(ltimerUint32 option, ltimerUint32 value);

    extern      HRESULT LTIMER_FUNCTION ltimerCreate(ltimer_t *ltimer);
    extern      HRESULT LTIMER_FUNCTION ltimerDestroy(ltimer_t *ltimer);
    extern      HRESULT LTIMER_FUNCTION ltimerReset(ltimer_t ltimer);

    extern ltimerUint64 LTIMER_FUNCTION ltimerGetCurrentTime(ltimer_t ltimer);
    extern ltimerUint64 LTIMER_FUNCTION ltimerGetLastReportedTime(ltimer_t ltimer);

    extern      HRESULT LTIMER_FUNCTION ltimerSleep(ltimer_t ltimer, ltimerUint64 ticks);
    extern      HRESULT LTIMER_FUNCTION ltimerSleepUntil(ltimer_t ltimer, ltimerUint64 time);

    extern      HRESULT LTIMER_FUNCTION ltimerGetOption(ltimer_t ltimer, ltimerUint32 option, ltimerUint32 *value);
    extern      HRESULT LTIMER_FUNCTION ltimerSetOption(ltimer_t ltimer, ltimerUint32 option, ltimerUint32 value);

    	#define LTIMER_OPTION_INVALID               (0)
    	#define LTIMER_OPTION_CALIBRATION_PERIOD    (1) /* in ms */
    	#define LTIMER_OPTION_RECALIBRATION_PERIOD  (2) /* in ms */
    	#define LTIMER_OPTION_LAST_GLOBAL_ONLY      (2)

    	#define LTIMER_OPTION_TIMER_RESOLUTION      (3)
    	#define LTIMER_OPTION_TIMING_MODE           (4) /* see below */
    	#define LTIMER_OPTION_LONG_SLEEP_THRESHOLD  (5) /* in ms */
    	#define LTIMER_OPTION_SHORT_SLEEP_THRESHOLD (6) /* in ms */

    	#define LTIMER_OPTION_LAST                  (6)

    	#define LTIMER_TIMING_MODE_INVALID      (0)
    	#define LTIMER_TIMING_MODE_PERFORMANCE  (1)
    	#define LTIMER_TIMING_MODE_SAFETY       (2)

    #ifdef __cplusplus

    inline __declspec(naked) ltimerUint64 ltimerGetRDTSC(void)
    	{
    	__asm
    		{
    		push ebx    // cpuid overwrites ebx
    		push ecx    // and ecx

    		// CPUID: flush the out-of-order instruction pipeline
    		__asm __emit 0x0F __asm __emit 0xA2

    		// RDTSC: and get the current cycle into edx:eax
    		__asm __emit 0x0F __asm __emit 0x31

    		pop ecx
    		pop ebx

    		ret
    		}
    	}

    };
    #endif /* __cplusplus */

    #endif /* __LTIMER_H */

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.