Boost logo

Boost Users :

From: Aaron W. LaFramboise (aaronrabiddog51_at_[hidden])
Date: 2004-05-06 02:46:30


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


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net