最快的定时分辨率系统

时间:2010-07-02 01:53:34

标签: c++ c windows time

C / C ++程序员可以使用的最快的计时系统是什么?

例如:
time()将给出1970年1月1日00:00以来的秒数 Windows上的GetTickCount()将给出自系统启动时间以来的时间(以毫秒为单位),但限制为49.7天(之后它只会回到零)。

我想获取当前时间,或者自系统/应用启动时间开始计时,以毫秒为单位。

最大的问题是方法的开销 - 我需要最轻的一个,因为我将要每秒多次调用它。

我的情况是我有一个工作线程,而那个工作线程我发布了待处理的工作。每个工作都有一个“执行时间”。因此,我不关心时间是当前的“实际”时间还是系统正常运行时间以来的时间 - 它必须是线性的和轻的。

编辑:

unsigned __int64 GetTickCountEx()
{
    static DWORD dwWraps = 0;
    static DWORD dwLast = 0;

    DWORD dwCurrent = 0;

    timeMutex.lock();

    dwCurrent = GetTickCount();
    if(dwLast > dwCurrent)
        dwWraps++;

    dwLast = dwCurrent;

    unsigned __int64 timeResult = ((unsigned __int64)0xFFFFFFFF * dwWraps) + dwCurrent;

    timeMutex.unlock();

    return timeResult;
}

10 个答案:

答案 0 :(得分:15)

对于时间安排,current Microsoft recommendation将使用QueryPerformanceCounter& QueryPerformanceFrequency

这将为您提供优于毫秒级的时序。如果系统不支持高分辨率计时器,则默认为毫秒(与GetTickCount相同)。

Here is a short Microsoft article with examples of why you should use it:)

答案 1 :(得分:5)

我最近有这个问题并做了一些研究。好消息是所有三个主要操作系统都提供某种高分辨率计时器。坏消息是它是每个系统上不同的API调用。对于POSIX操作系统,您希望使用clock_gettime()。但是,如果您使用的是Mac OS X,则不支持此操作,您必须使用mach_get_time()。对于Windows,请使用QueryPerformanceCounter。或者,对于支持OpenMP的编译器,您可以使用omp_get_wtime(),但它可能无法提供您正在寻找的分辨率。

我还发现fftw.org(www.fftw.org/cycle.h)中的cycle.h很有用。

下面是一些使用一些丑陋的#ifdef语句在每个操作系统上调用计时器的代码。用法很简单:定时器t; t.tic(); SomeOperation(); t.toc( “消息”);它将以秒为单位打印出已用时间。

#ifndef TIMER_H
#define TIMER_H

#include <iostream>
#include <string>
#include <vector>

# if  (defined(__MACH__) && defined(__APPLE__))
#   define _MAC
# elif (defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(_WIN64))
#   define _WINDOWS
#   ifndef WIN32_LEAN_AND_MEAN
#     define WIN32_LEAN_AND_MEAN
#   endif
#endif

# if defined(_MAC)
#    include <mach/mach_time.h>
# elif defined(_WINDOWS)
#    include <windows.h>
# else
#    include <time.h>
# endif


#if defined(_MAC)
  typedef uint64_t timer_t;
  typedef double   timer_c;

#elif defined(_WINDOWS)
  typedef LONGLONG      timer_t;
  typedef LARGE_INTEGER timer_c;

#else
  typedef double   timer_t;
  typedef timespec timer_c;
#endif

  //==============================================================================
  // Timer
  // A quick class to do benchmarking.
  // Example: Timer t;  t.tic();  SomeSlowOp(); t.toc("Some Message");

  class Timer {
  public:
    Timer();

    inline void tic();
    inline void toc();
    inline void toc(const std::string &msg);

    void print(const std::string &msg);
    void print();
    void reset();
    double getTime();

  private:
    timer_t start;
    double duration;
    timer_c ts;
    double conv_factor;
    double elapsed_time;
  };



  Timer::Timer() {

#if defined(_MAC)
    mach_timebase_info_data_t info;
    mach_timebase_info(&info);

    conv_factor = (static_cast<double>(info.numer))/
                  (static_cast<double>(info.denom));
    conv_factor = conv_factor*1.0e-9;

#elif defined(_WINDOWS)
    timer_c freq;
    QueryPerformanceFrequency(&freq);
    conv_factor = 1.0/(static_cast<double>freq.QuadPart);

#else
    conv_factor = 1.0;
#endif

    reset();
  }

  inline void Timer::tic() {

#if defined(_MAC)
    start = mach_absolute_time();

#elif defined(_WINDOWS)
    QueryPerformanceCounter(&ts);
    start = ts.QuadPart;

#else
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
    start = static_cast<double>(ts.tv_sec) + 1.0e-9 *
            static_cast<double>(ts.tv_nsec);

#endif
  }

  inline void Timer::toc() {
#if defined(_MAC)
    duration =  static_cast<double>(mach_absolute_time() - start);

#elif defined(_WINDOWS)
    QueryPerformanceCounter(&qpc_t);
    duration = static_cast<double>(qpc_t.QuadPart - start);

#else
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
    duration = (static_cast<double>(ts.tv_sec) + 1.0e-9 *
                static_cast<double>(ts.tv_nsec)) - start;

#endif

    elapsed_time = duration*conv_factor;
  }

  inline void Timer::toc(const std::string &msg) { toc(); print(msg); };

  void Timer::print(const std::string &msg) {
    std::cout << msg << " "; print();
  }

  void Timer::print() {
    if(elapsed_time) {
      std::cout << "elapsed time: " << elapsed_time << " seconds\n";
    }
  }

  void Timer::reset() { start = 0; duration = 0; elapsed_time = 0; }
  double Timer::getTime() { return elapsed_time; }


#if defined(_WINDOWS)
# undef WIN32_LEAN_AND_MEAN
#endif

#endif // TIMER_H

答案 2 :(得分:4)

GetSystemTimeAsFileTime是最快的资源。可以获得其粒度 通过调用填充 lpTimeIncrement GetSystemTimeAdjustment。作为文件时间的系统时间为100ns单位,并按 TimeIncrement 递增。 TimeIncrement 可能会有所不同,具体取决于多媒体计时器界面的设置。

致电timeGetDevCaps将披露时间服务的功能。它回来了 最小支持的中断周期的值 wPeriodMin 。使用 wPeriodMin 作为参数调用timeBeginPeriod将使系统设置为以尽可能高的中断频率(通常为~1ms)运行。这将同时强制GetSystemTimeAsFileTime返回的系统文件时间的时间增量更小。其粒度将在1毫秒(10000 100ns单位)的范围内。

出于您的目的,我建议采用这种方法。

QueryPerformanceCounter选择是有问题的,因为它的频率不是 通过两种方法准确:首先,它与QueryPerformanceFrequency给出的值偏离硬件特定的偏移量。这种偏移可以很容易 几百ppm,这意味着转换为时间将包含每秒几百微秒的误差。其次它有热漂移。这种装置的漂移可以很容易地达到几个ppm。这种方式另一种 - 热量依赖 - 错误 添加了几个us / s。

只要〜1ms的分辨率足够且主要问题是开销, GetSystemTimeAsFileTime是迄今为止最好的解决方案。

当微秒很重要时,你必须走得更远,看看更多细节。亚毫秒时间服务在Windows Timestamp Project

中描述

答案 3 :(得分:3)

如果您只是担心GetTickCount()溢出,那么您可以像这样包装它:

DWORDLONG GetLongTickCount(void)
{
    static DWORDLONG last_tick = 0;
    DWORD tick = GetTickCount();

    if (tick < (last_tick & 0xffffffff))
        last_tick += 0x100000000;

    last_tick = (last_tick & 0xffffffff00000000) | tick;
    return last_tick;
}

如果要从多个线程调用此方法,则需要锁定对last_tick变量的访问权限。只要您每49.7天至少调用GetLongTickCount()一次,它就会检测到溢出。

答案 4 :(得分:1)

如果您专门针对Windows,我建议您使用GetSystemTimeAsFileTime API。它通常比GetSystemTime快,并且具有相同的精度(大约10-15毫秒 - 不看分辨率);几年前我在Windows XP下做了一个基准测试,它的速度提高了50到100倍。

唯一的缺点是您可能必须使用例如将返回的FILETIME结构转换为时钟时间。 FileTimeToSystemTime如果您需要以更人性化的格式访问返回的时间。另一方面,只要您不需要实时转换时间,您就可以离线或以“懒惰”方式执行此操作(例如,只转换您需要显示/处理的时间戳,以及只有当你真正需要它们时。)

QueryPerformanceCounter可能是其他人提到的一个不错的选择,但根据底层硬件支持,开销可能相当大。在我的基准测试中,我提到上面的QueryPerformanceCounter调用比GetSystemTimeAsFileTime调用慢25-200倍。此外,存在一些可靠性问题,例如,报告here

所以,总结一下:如果你能处理10-15毫秒的精度,我建议你使用GetSystemTimeAsFileTime。如果你需要更好的东西,我会选择QueryPerformanceCounter。

小免责声明:我没有在Windows XP以后的版本中执行任何基准测试。我建议你自己做一些基准测试。

答案 5 :(得分:0)

在Linux上你得到微秒:

struct timeval tv;
int res = gettimeofday(&tv, NULL);
double tmp = (double) tv.tv_sec + 1e-6 * (double) tv.tv_usec;

在Windows上,只有毫秒可用:

SYSTEMTIME st;
GetSystemTime(&st);
tmp += 1e-3 * st.wMilliseconds;

return tmp;

这来自R的datetime.c(为简洁起见,已经过编辑)。

然后当然有Boost's Date_Time在一些系统上可以有纳秒分辨率(详情herehere)。

答案 6 :(得分:0)

POSIX支持clock_gettime(),它使用具有纳秒分辨率的struct timespec。你的系统是否真的支持细粒度的分辨率更值得商榷,但我相信这是具有最高分辨率的标准调用。并非所有系统都支持它,并且它有时被很好地隐藏(Solaris,IIRC上的库“-lposix4”。


更新(2016-09-20):

  • Mac OS X 10.6.4不支持clock_gettime(),也没有任何其他版本的Mac OS X,包括Mac OS X 10.11.6 El Capitan。但是,从macOS Sierra 10。12(2016年9月发布)开始,macOS确实具有函数clock_gettime()和手册页。实际分辨率(CLOCK_MONOTONIC)仍为微秒;较小的单位都是零。这由clock_getres()确认,其报告分辨率为1000纳秒,也就是1微秒。

macOS Sierra上clock_gettime()的手册页提到了mach_absolute_time(),以获得高分辨率计时。有关其他地方的更多信息,请参阅Technical Q&A QA1398: Mach Absolute Time Units和(在SO上)What is mach_absolute_time() based on on iPhone?

答案 7 :(得分:0)

如果你的目标是足够晚的操作系统版本,那么你可以使用GetTickCount64(),其环绕点比GetTickCount()高得多。您还可以在GetTickCount64()之上构建GetTickCount()版本。

答案 8 :(得分:0)

您是否查看了此MSDN文章中的代码?

http://msdn.microsoft.com/en-us/magazine/cc163996.aspx

我使用VC2005和C ++ Builder XE在Windows 7 64位机器上编译此代码,但在执行时,它会锁定我的机器;没有调试到足以弄明白为什么。看起来过于复杂。 UG模板模板模板......

答案 9 :(得分:0)

在Mac OS X上,您可以简单地使用UInt32 TickCount(void)来获取刻度。