
I have prototype implementations for a few simple Windows clocks I have in mind in works. I'll post when I get a chance to finish something. In the meantime, perhaps this might be interesting to someone. I wrote this two days ago to assess the capabilities of the clocks on Windows to determine what might be the most suitable as a time source. It outputs a combination of hardcoded information about the documented capabilities of clocks, plus information it has determined real-time about the capabilities of clocks from the OS. It should be standard C++ with documented Windows extensions. I tested on GCC, but it would probably work for any Windows compiler with little or no modifications. The rdtsc support will only work on GCC, though. Note that the implementation is very naïve, and there are probably numerous flaws. Its the best I could come up with without wasting a whole lot of time. I am certainly interested in possible improvements. It takes about 30 seconds to run. On my platform (Windows XP Professional, GCC 3.3.2, MSVCRT runtime, Intel Pentium 4 1.4GHz), it has the following output. I don't have access to a whole lot of other machines presently, so if you have a Windows computer where these tests show interesting results, I would certainly be interested in privately seeing the output on your machine. Measuring timer precision for all system timers. Standard C time() Granularity: 1 second Precision: 1 second Apparent precision: 1 seconds Apparent jitter: 0 seconds Standard C clock() Granularity: Unknown Precision: "implementation's best approximation to processor time" Apparent precision: 0.01 seconds Apparent jitter: 0 seconds Windows system time GetSystemTime() Granularity: 100 nanoseconds Precision: approximately 10 milliseconds (Approximately 55ms on Windows 95/98/ME, 15ms on Windows NT 3.1.) (Presently reported as 10.0144 milliseconds.) Apparent precision: 0.010066666666666666 seconds Apparent jitter: 0.00014285714285714287 seconds Windows system time in file time GetSystemTimeAsFileTime() Granularity: 100 nanoseconds Precision: Same as system time Apparent precision: 0.0100144 seconds Apparent jitter: 0 seconds Windows tick counter GetTickCount() Granularity: 1 millisecond Precision: Same as system time Apparent precision: 0.01 seconds Apparent jitter: 0 seconds Windows Multimedia Timer timeGetTime() Granularity: 1 millisecond Precision: 1 millisecond Apparent precision: 0.0016666666666666668 seconds Apparent jitter: 0.00078571428571428575 seconds Windows High-Performance Timer QueryPerformanceCounter() Granularity: Unknown (Presently reported as 2.7936511484001459e-007 seconds.) Precision: Unknown Apparent precision: 1.6948150300294219e-006 seconds Apparent jitter: 3.9909302120002083e-008 seconds Pentium Timestamp Counter RDTSC Granularity: Unknown (Presently reported as 7.1643876564487315e-010 seconds.) Precision: Same as granularity Apparent precision: 1.2513797106597118e-007 seconds Apparent jitter: 1.5352259263818709e-008 seconds Everything up to the EOF is the source of win32_precision.cpp: const unsigned long iterations = 16; const unsigned long calibrate = 16; #include <cmath> #include <ctime> #include <limits> #include <iomanip> #include <iostream> #include <utility> #include <windows.h> class time_timer { public: void set(); bool operator==(const time_timer &t) const { return time == t.time; } std::time_t operator-(const time_timer &t) const { return time - t.time; } static void warm(); static long double to_seconds(long double); private: std::time_t time; }; void time_timer::set() { time = ::time(0); } void time_timer::warm() { ::time(0); ::time(0); ::time(0); } long double time_timer::to_seconds(long double t) { return t; } class clock_timer { public: void set(); bool operator==(const clock_timer &t) const { return time == t.time; } std::clock_t operator-(const clock_timer &t) const { return time - t.time; } static void warm(); static long double to_seconds(long double); private: std::clock_t time; }; void clock_timer::set() { time = ::clock(); } void clock_timer::warm() { ::clock(); ::clock(); ::clock(); } long double clock_timer::to_seconds(long double t) { return t/CLOCKS_PER_SEC; } class systemtime_timer { public: void set(); bool operator==(const systemtime_timer &t) const { return time.wYear == t.time.wYear && time.wMonth == t.time.wMonth && time.wDayOfWeek == t.time.wDayOfWeek && time.wDay == t.time.wDay && time.wHour == t.time.wHour && time.wMinute == t.time.wMinute && time.wSecond == t.time.wSecond && time.wMilliseconds == t.time.wMilliseconds; } ULONGLONG operator-(const systemtime_timer &t) const { FILETIME a, b; SystemTimeToFileTime(&time, &a); SystemTimeToFileTime(&t.time, &b); return reinterpret_cast<const ULARGE_INTEGER *>(&a)->QuadPart - reinterpret_cast<const ULARGE_INTEGER *>(&b)->QuadPart; } static void warm(); static long double to_seconds(long double); private: SYSTEMTIME time; }; void systemtime_timer::set() { GetSystemTime(&time); } void systemtime_timer::warm() { SYSTEMTIME dummy; GetSystemTime(&dummy); GetSystemTime(&dummy); GetSystemTime(&dummy); } long double systemtime_timer::to_seconds(long double t) { return t/10000000; } class systemtimeaft_timer { public: void set(); bool operator==(const systemtimeaft_timer &t) const { return time.dwLowDateTime == t.time.dwLowDateTime && time.dwHighDateTime == t.time.dwHighDateTime; } ULONGLONG operator-(const systemtimeaft_timer &t) const { return reinterpret_cast<const ULARGE_INTEGER *>(&time)->QuadPart - reinterpret_cast<const ULARGE_INTEGER *>(&t.time)->QuadPart; } static void warm(); static long double to_seconds(long double); private: FILETIME time; }; void systemtimeaft_timer::set() { GetSystemTimeAsFileTime(&time); } void systemtimeaft_timer::warm() { FILETIME dummy; GetSystemTimeAsFileTime(&dummy); GetSystemTimeAsFileTime(&dummy); GetSystemTimeAsFileTime(&dummy); } long double systemtimeaft_timer::to_seconds(long double t) { return t/10000000; } class gettickcount_timer { public: void set(); bool operator==(const gettickcount_timer &t) const { return time == t.time; } DWORD operator-(const gettickcount_timer &t) const { return time - t.time; } static void warm(); static long double to_seconds(long double); private: DWORD time; }; void gettickcount_timer::set() { time = ::GetTickCount(); } void gettickcount_timer::warm() { ::timeGetTime(); ::timeGetTime(); ::timeGetTime(); } long double gettickcount_timer::to_seconds(long double t) { return t/1000; } class timegettime_timer { public: void set(); bool operator==(const timegettime_timer &t) const { return time == t.time; } DWORD operator-(const timegettime_timer &t) const { return time - t.time; } static void warm(); static long double to_seconds(long double); private: DWORD time; }; void timegettime_timer::set() { time = ::timeGetTime(); } void timegettime_timer::warm() { UINT i = 1; for(; i != 100; ++i) { if(timeBeginPeriod(i) != TIMERR_NOCANDO) break; } if(i != 1) std::cerr << "timegettime_timer::warm(): Unable to set timer resolution " "to 1\n"; ::timeGetTime(); ::timeGetTime(); ::timeGetTime(); } long double timegettime_timer::to_seconds(long double t) { return t/1000; } class queryperformance_timer { public: void set(); bool operator==(const queryperformance_timer &t) const { return time.QuadPart == t.time.QuadPart; } LONGLONG operator-(const queryperformance_timer &t) const { return time.QuadPart - t.time.QuadPart; } static void warm(); static long double to_seconds(long double); private: LARGE_INTEGER time; }; void queryperformance_timer::set() { ::QueryPerformanceCounter(&time); } void queryperformance_timer::warm() { LARGE_INTEGER dummy; if(::QueryPerformanceCounter(&dummy) == 0) std::cerr << "timegettime_timer::warm(): System does not support " "high-resolution timer.\n"; ::QueryPerformanceCounter(&dummy); ::QueryPerformanceCounter(&dummy); } long double queryperformance_timer::to_seconds(long double t) { LARGE_INTEGER freq; ::QueryPerformanceFrequency(&freq); return t/freq.QuadPart; } #if defined(__GNUC__) && (defined(__i586__) || defined(__i686__)) __extension__ typedef unsigned long long rdtsc_t; long double cycles_per_second; long double measure_cycles_per_second() { std::time_t last = std::time(0); std::clock_t clock_start = std::clock(); rdtsc_t rdtsc_start; __asm__ __volatile__( "rdtsc" : "=A" (rdtsc_start)); while(std::time(0) != static_cast<std::time_t>(last + calibrate)) {} rdtsc_t rdtsc_stop; __asm__ __volatile__( "rdtsc" : "=A" (rdtsc_stop)); std::clock_t clock_stop = std::clock(); return static_cast<long double>(rdtsc_stop - rdtsc_start) / (clock_stop - clock_start) * CLOCKS_PER_SEC; } class rdtsc_timer { public: void set(); bool operator==(const rdtsc_timer &t) const { return time == t.time; } rdtsc_t operator-(const rdtsc_timer &t) const { return time - t.time; } static void warm(); static long double to_seconds(long double); private: rdtsc_t time; }; void rdtsc_timer::set() { __asm__ __volatile__( "rdtsc" : "=A" (time)); } void rdtsc_timer::warm() { rdtsc_t dummy; __asm__ __volatile__( "rdtsc" : "=A" (dummy)); __asm__ __volatile__( "rdtsc" : "=A" (dummy)); __asm__ __volatile__( "rdtsc" : "=A" (dummy)); } long double rdtsc_timer::to_seconds(long double t) { return t / cycles_per_second; } #endif template<typename timer> std::pair<long double, long double> get_precision() { timer::warm(); timer last; last.set(); long double sum_delta = 0; long double sum_delta_delta = 0; long double last_delta = 0; for(unsigned long i = 0; i != iterations; ++i) { timer now; do { now.set(); } while(now == last); if(i) { long double delta = now - last; sum_delta += delta; if(last_delta) sum_delta_delta += std::fabs(delta - last_delta); last_delta = delta; } last = now; } long double mean = timer::to_seconds(sum_delta/(iterations - 1)); long double jitter = timer::to_seconds(sum_delta_delta/(iterations - 2)); return std::make_pair(mean, jitter); } int main() { std::cout << std::setprecision(std::numeric_limits<long double>::digits10); std::cout << "Measuring timer precision for all system timers.\n\n\n"; std::pair<long double, long double> p; std::cout << "Standard C time()\n" " Granularity: 1 second\n" " Precision: 1 second" << std::endl; p = get_precision<time_timer>(); std::cout << " Apparent precision: " << p.first << " seconds\n" " Apparent jitter: " << p.second << " seconds\n\n"; std::cout << "Standard C clock()\n" " Granularity: Unknown\n" " Precision: \"implementation's best approximation to processor time\"" << std::endl; p = get_precision<clock_timer>(); std::cout << " Apparent precision: " << p.first << " seconds\n" " Apparent jitter: " << p.second << " seconds\n\n"; DWORD adjustment, increment; BOOL disabled; ::GetSystemTimeAdjustment(&adjustment, &increment, &disabled); std::cout << "Windows system time GetSystemTime()\n" " Granularity: 100 nanoseconds\n" " Precision: approximately 10 milliseconds\n" " (Approximately 55ms on Windows 95/98/ME, 15ms on Windows NT 3.1.)\n" " (Presently reported as " << static_cast<long double>(increment)/10000 << " milliseconds.)" << std::endl; p = get_precision<systemtime_timer>(); std::cout << " Apparent precision: " << p.first << " seconds\n" " Apparent jitter: " << p.second << " seconds\n\n"; std::cout << "Windows system time in file time GetSystemTimeAsFileTime()\n" " Granularity: 100 nanoseconds\n" " Precision: Same as system time" << std::endl; p = get_precision<systemtimeaft_timer>(); std::cout << " Apparent precision: " << p.first << " seconds\n" " Apparent jitter: " << p.second << " seconds\n\n"; std::cout << "Windows tick counter GetTickCount()\n" " Granularity: 1 millisecond\n" " Precision: Same as system time" << std::endl; p = get_precision<gettickcount_timer>(); std::cout << " Apparent precision: " << p.first << " seconds\n" " Apparent jitter: " << p.second << " seconds\n\n"; std::cout << "Windows Multimedia Timer timeGetTime()\n" " Granularity: 1 millisecond\n" " Precision: 1 millisecond" << std::endl; p = get_precision<timegettime_timer>(); std::cout << " Apparent precision: " << p.first << " seconds\n" " Apparent jitter: " << p.second << " seconds\n\n"; LARGE_INTEGER freq; ::QueryPerformanceFrequency(&freq); std::cout << "Windows High-Performance Timer QueryPerformanceCounter()\n" " Granularity: Unknown\n" " (Presently reported as " << static_cast<long double>(1)/freq.QuadPart << " seconds.)\n" " Precision: Unknown" << std::endl; p = get_precision<queryperformance_timer>(); std::cout << " Apparent precision: " << p.first << " seconds\n" " Apparent jitter: " << p.second << " seconds\n\n"; #if defined(__GNUC__) && (defined(__i586__) || defined(__i686__)) std::cout << "Pentium Timestamp Counter RDTSC\n" " Granularity: Unknown\n" " (Presently reported as " << std::flush; cycles_per_second = measure_cycles_per_second(); std::cout << static_cast<long double>(1)/cycles_per_second << " seconds.)\n" " Precision: Same as granularity" << std::endl; p = get_precision<rdtsc_timer>(); std::cout << " Apparent precision: " << p.first << " seconds\n" " Apparent jitter: " << p.second << " seconds\n\n"; #endif return 0; } EOF Any comments, let me know. Aaron W. LaFramboise