代码之家  ›  专栏  ›  技术社区  ›  Poni

最快时间分辨系统

  •  9
  • Poni  · 技术社区  · 14 年前

    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 回复  |  直到 8 年前
        1
  •  15
  •   porges    14 年前

    对于时间, current Microsoft recommendation 是使用 QueryPerformanceCounter 和; QueryPerformanceFrequency .

    这将给你比毫秒更好的时间。如果系统不支持高分辨率计时器,则默认为毫秒(与 GetTickCount )

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

        2
  •  5
  •   reso    14 年前

    我最近有这个问题,做了一些研究。好消息是,所有三个主要操作系统都提供某种高分辨率计时器。坏消息是,它是每个系统上不同的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语句。使用非常简单:timer t;t.tic();someoperation();t.toc(“message”);它将以秒为单位打印所用时间。

    #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
    
        3
  •  4
  •   caf    14 年前

    如果你只是担心 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 变量。只要你打电话 GetLongTickCount() 至少每49.7天检测一次溢出。

        4
  •  4
  •   Arno    12 年前

    GetSystemTimeAsFileTime 是最快的资源。它的粒度可以得到 打电话到 GetSystemTimeAdjustment 哪些填充 LPTIME增量 . 作为文件时间的系统时间有100ns单位,增量为 时间增量 . 时间增量 可以改变,这取决于多媒体计时器接口的设置。

    对…的呼唤 timeGetDevCaps 将披露时间服务的能力。它返回 一个值 沃尔明 对于支持的最小中断周期。对…的呼唤 timeBeginPeriod 具有 沃尔明 as参数将设置系统以尽可能高的中断频率(通常为~1毫秒)运行。本遗嘱 强制返回系统文件时间的时间增量 GetSystemTimeAsFileTime 更小。其粒度范围为1ms(10000100ns单位)。

    为了你的目的,我建议采用这种方法。

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

    只要~1毫秒的分辨率足够,主要问题是开销, 获取系统时间文件时间 是目前最好的解决方案。

    当微秒很重要时,你必须走更长的路才能看到更多的细节。亚毫秒时间服务在 Windows Timestamp Project

        5
  •  1
  •   rjnilsson    14 年前

    我建议你使用 GetSystemTimeAsFileTime 如果您是专门针对Windows的,则使用API。通常比 GetSystemTime 并且具有相同的精度(大约10-15毫秒-不要看分辨率);几年前我在WindowsXP下做基准测试时,速度大约是50-100倍。

    唯一的缺点是,您可能必须使用例如 FileTimeToSystemTime 如果您需要以更人性化的格式访问返回的时间。另一方面,只要您不需要实时转换这些时间,就可以离线或以“懒惰”的方式进行转换(例如,只转换需要显示/处理的时间戳,并且仅在您实际需要时转换)。

    QueryPerformanceCounter 正如其他人提到的,这可能是一个不错的选择,但是开销可能很大,这取决于底层的硬件支持。在我的基准测试中,我提到上面的queryperformanceCounter调用比对getSystemTimeasFileTime的调用慢25-200倍。此外,还有一些可靠性问题,如报告的 here .

    所以,总之:如果您能够处理10-15毫秒的精度,我建议您使用getSystemTimeasFileTime。如果你需要比这更好的东西,我会去查询性能计数器。

    小免责声明:我没有在比xp sp3更高版本的Windows下执行任何基准测试。我建议你自己做一些基准测试。

        6
  •  0
  •   Dirk is no longer here    14 年前

    在Linux上,您可以获得微秒:

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

    在Windows上,只有millseconds可用:

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

    这来自 R 的datetime.c(为了简洁而被编辑)。

    当然还有 Boost's Date_Time 在某些系统上具有纳秒级分辨率(细节 here here )

        7
  •  0
  •   Len Holgate    14 年前

    如果您的目标是一个足够晚的操作系统版本,那么您可以使用 GetTickCount64() 它的环绕点比 GetTickCount() . 您还可以简单地构建 GetTickCount64()。 在顶部 GETTICKCOUNT() .

        8
  •  0
  •   Eric    14 年前

    你看过这篇文章中的代码了吗?

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

    我使用VC2005和C++Builder XE在Windows 7 64位机上编译此代码,但在执行时,它锁定了我的机器;还没有进行足够的调试来找出原因。这似乎太复杂了。UG模板模板模板…

        9
  •  0
  •   Robby Shaw    12 年前

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

        10
  •  0
  •   Community CDub    7 年前

    POSIX支持 clock_gettime() 其中使用 struct timespec 具有纳秒级的分辨率。您的系统是否真的支持这种细粒度的A分辨率更值得商榷,但我相信这是最高分辨率的标准调用。不是所有的系统都支持它,有时它隐藏得很好(库 -lposix4 '在Solaris上,IIRC)。


    更新(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也具有此功能。 CcLogyGETTIME() 最后是手册页。实际分辨率 CLOCK_MONOTONIC )仍然是微秒;较小的单位都是零。确认人: clock_getres() 报告的分辨率是1000纳秒,也就是1微秒。

    手册页 CcLogyGETTIME() 关于Macos Sierra的提到 mach_absolute_time() 作为获得高分辨率时间的一种方法。有关更多信息,请参阅 Technical Q&A QA1398: Mach Absolute Time Units (这样) What is mach_absolute_time() based on on iPhone?