C++ 跨平台高分辨率计时器

Posted

技术标签:

【中文标题】C++ 跨平台高分辨率计时器【英文标题】:C++ Cross-Platform High-Resolution Timer 【发布时间】:2010-12-02 01:00:52 【问题描述】:

我希望在 C++ 中实现一个简单的计时器机制。该代码应该可以在 Windows 和 Linux 中运行。分辨率应尽可能精确(至少毫秒精度)。这将用于简单地跟踪时间的流逝,而不是实现任何类型的事件驱动设计。完成此任务的最佳工具是什么?

【问题讨论】:

更具体。您是在计时函数调用还是希望在指定时间段后接收某种信号。这些都是“简单”的计时器应用程序,但它们的实现方式非常不同。请注意,引号中“简单”的使用:通用计算机中的计时从来都不是“简单”。 C版***.com/questions/361363/… 【参考方案1】:

旧问题的更新答案:

在 C++11 中,您可以通过以下方式轻松获得最高分辨率的计时器:

#include <iostream>
#include <chrono>
#include "chrono_io"

int main()

    typedef std::chrono::high_resolution_clock Clock;
    auto t1 = Clock::now();
    auto t2 = Clock::now();
    std::cout << t2-t1 << '\n';

示例输出:

74 nanoseconds

“chrono_io”是缓解这些新类型的 I/O 问题的扩展,可免费使用here。

在 boost 中还有一个&lt;chrono&gt; 的实现(可能仍在主干上,不确定它是否已发布)。

更新

这是对 Ben 在下面的评论的回应,即后续调用 std::chrono::high_resolution_clock 在 VS11 中需要几毫秒。以下是&lt;chrono&gt; 兼容的解决方法。但是它只适用于 Intel 硬件,您需要使用内联汇编(执行此操作的语法因编译器而异),并且您必须将机器的时钟速度硬连线到时钟中:

#include <chrono>

struct clock

    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2800000000>          period; // My machine is 2.8 GHz
    typedef std::chrono::duration<rep, period> duration;
    typedef std::chrono::time_point<clock>     time_point;
    static const bool is_steady =              true;

    static time_point now() noexcept
    
        unsigned lo, hi;
        asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
        return time_point(duration(static_cast<rep>(hi) << 32 | lo));
    

private:

    static
    unsigned
    get_clock_speed()
    
        int mib[] = CTL_HW, HW_CPU_FREQ;
        const std::size_t namelen = sizeof(mib)/sizeof(mib[0]);
        unsigned freq;
        size_t freq_len = sizeof(freq);
        if (sysctl(mib, namelen, &freq, &freq_len, nullptr, 0) != 0)
            return 0;
        return freq;
    

    static
    bool
    check_invariants()
    
        static_assert(1 == period::num, "period must be 1/freq");
        assert(get_clock_speed() == period::den);
        static_assert(std::is_same<rep, duration::rep>::value,
                      "rep and duration::rep must be the same type");
        static_assert(std::is_same<period, duration::period>::value,
                      "period and duration::period must be the same type");
        static_assert(std::is_same<duration, time_point::duration>::value,
                      "duration and time_point::duration must be the same type");
        return true;
    

    static const bool invariants;
;

const bool clock::invariants = clock::check_invariants();

所以它不是便携式的。但是,如果您想在自己的英特尔硬件上试验高分辨率时钟,没有比这更好的了。尽管预先警告,今天的时钟速度可以动态变化(它们并不是真正的编译时间常数)。使用多处理器机器,您甚至可以从不同的处理器获取时间戳。但是,在我的硬件上进行的实验仍然运行良好。如果您被毫秒分辨率困扰,这可能是一种解决方法。

根据您的 cpu 时钟速度(如您报告的那样),此时钟具有持续时间。 IE。对我来说,这个时钟每 1/2,800,000,000 秒滴答一次。如果您愿意,您可以将其转换为纳秒(例如):

using std::chrono::nanoseconds;
using std::chrono::duration_cast;
auto t0 = clock::now();
auto t1 = clock::now();
nanoseconds ns = duration_cast<nanoseconds>(t1-t0);

转换将截断 CPU 周期的一部分以形成纳秒。其他舍入模式也是可能的,但这是另一个主题。

对我来说,这将返回低至 18 个时钟滴答的持续时间,截断为 6 纳秒。

我在上面的时钟中添加了一些“不变检查”,其中最重要的是检查clock::period 是否适用于机器。同样,这不是可移植的代码,但如果你使用这个时钟,你已经承诺了。此处显示的私有 get_clock_speed() 函数获取 OS X 上的最大 cpu 频率,这应该与 clock::period 的常数分母相同。

当您将此代码移植到新机器上而忘记将clock::period 更新为新机器的速度时,添加它可以为您节省一点调试时间。所有检查都在编译时或程序启动时完成。所以至少不会影响clock::now()的性能。

【讨论】:

不幸的是,在 Visual Studio 11 中,high_resolution_clock 的最短非零间隔是几毫秒。 我花了几秒钟才下沉……在时钟速度只有几分之一纳秒的平台上需要数百万纳秒。哇!!!我希望看到可以测量几分之一纳秒的平台。我认为我几十纳秒的结果并不令人印象深刻。 有人知道在编译时获取 CPU 频率的方法吗?另外......这些天,cpu频率不能在运行时改变,涡轮模式等等?也许这会使这种方法无效?不过,我确实需要一个像样的 VS11 计时器,呃。 @Dave:是的,cpu 频率可以动态变化(我在答案中说明了这一点)。我在使用它时的实验通常是围绕我要测量的东西的一个紧密循环。如此紧密的循环,至少对于我的平台而言,通常会将 cpu 频率提升到最大值,并且该最大值通常是编译时间常数(从 cpu 规范中读取)。因此,对于这种基准测试,这可能是一种有效的技术。但显然这不是通用用途。这不是我建议运送的东西。仅用于调查目的。 我在使用 VS2017 的 Windows 上获得了 600-1200 纳秒,它似乎正在使用高性能计时器。所以看来这个1ms分辨率的问题已经不是问题了。【参考方案2】:

对于 C++03

Boost.Timer 可能有效,但它取决于 C 函数 clock,因此可能对您来说分辨率不够。

Boost.Date_Time 包含一个ptime class,以前在 Stack Overflow 上推荐过。请参阅 microsec_clock::local_timemicrosec_clock::universal_time 上的文档,但请注意“Win32 系统通常无法通过此 API 实现微秒级分辨率”。

STLsoft 提供围绕操作系统特定 API 的精简跨平台(Windows 和 Linux/Unix)C++ 包装器。它的performance library 有几个类可以满足您的需要。 (要使其跨平台,请选择在 winstlunixstl 命名空间中都存在的类似 performance_counter 的类,然后使用与您的平台匹配的任何命名空间。)

对于 C++11 及以上版本

std::chrono 库内置了此功能。有关详细信息,请参阅 @HowardHinnant 的 this answer。

【讨论】:

由于这是一个著名的问题/答案,因此更新可能会很棒。具体来说,这可以使用现代 C++ 功能以标准且可移植的方式实现,例如 &lt;chrono&gt;&lt;thread&gt;?如果可能,怎么做?【参考方案3】:

Matthew Wilson 的STLSoft libraries 提供多种计时器类型,具有一致的接口,因此您可以即插即用。其中包括低成本但低分辨率的定时器,以及高分辨率但高成本的定时器。还有一些用于测量预线程时间和用于测量每个进程的时间,以及所有用于测量经过时间的工具。

几年前有一个详尽的article covering it in Dr. Dobb's,虽然它只涵盖Windows,那些在WinSTL 子项目中定义的。 STLSoft 还在 UNIXSTL 子项目中提供了 UNIX 定时器,您可以使用“PlatformSTL”一种,其中包括 UNIX 或 Windows 一种,如:

#include <platformstl/performance/performance_counter.hpp>
#include <iostream>

int main()

    platformstl::performance_counter c;

    c.start();
    for(int i = 0; i < 1000000000; ++i);
    c.stop();

    std::cout << "time (s): " << c.get_seconds() << std::endl;
    std::cout << "time (ms): " << c.get_milliseconds() << std::endl;
    std::cout << "time (us): " << c.get_microseconds() << std::endl;

HTH

【讨论】:

【参考方案4】:

StlSoft 开源库在 windows 和 linux 平台上都提供了相当的good timer。如果您希望它自己实现,只需查看它们的来源即可。

【讨论】:

【参考方案5】:

ACE 库还具有便携式高分辨率计时器。

用于高分辨率计时器的 Doxygen:http://www.dre.vanderbilt.edu/Doxygen/5.7.2/html/ace/a00244.html

【讨论】:

【参考方案6】:

我已经看到这实现了几次作为闭源内部解决方案......它们都求助于 #ifdef 解决方案,一方面围绕本机 Windows 高分辨率计时器和使用 struct timeval 的 Linux 内核计时器(参见man timeradd)。

您可以对此进行抽象,一些开源项目已经做到了——我看到的最后一个是CoinOR class CoinTimer,但肯定还有更多。

【讨论】:

我决定走这条路。您的链接已失效,所以我评论了一个仍在使用的链接:songho.ca/misc/timer/timer.html 啊,没有什么比对一个 8 年前的问题发表评论更重要了 :) 与此同时,我很幸运地使用了 Google 的 CCTZ 库,它建立在一些较新的 C++11 成语之上.【参考方案7】:

为此我强烈推荐 boost::posix_time 库。它支持各种分辨率的计时器,我相信它可以低至微秒

【讨论】:

【参考方案8】:

SDL2 具有出色的跨平台高分辨率计时器。但是,如果您需要亚毫秒级的精度,我编写了一个非常小的跨平台计时器库here。 它兼容 C++03 和 C++11/更高版本的 C++。

【讨论】:

【参考方案9】:

C++ 库问题的第一个答案一般是 BOOST:http://www.boost.org/doc/libs/1_40_0/libs/timer/timer.htm。这是做你想做的吗?可能不是,但这是一个开始。

问题是你想要便携,定时器功能在操作系统中不是通用的。

【讨论】:

【参考方案10】:

STLSoft 有一个 Performance Library,其中包括一组计时器类,其中一些适用于 UNIX 和 Windows。

【讨论】:

【参考方案11】:

我不确定你的要求,如果你想计算时间间隔,请看下面的线程

Calculating elapsed time in a C program in milliseconds

【讨论】:

【参考方案12】:

我发现这个看起来很有希望,而且非常简单,不确定是否有任何缺点:

https://gist.github.com/ForeverZer0/0a4f80fc02b96e19380ebb7a3debbee5

/* ----------------------------------------------------------------------- */
/*
Easy embeddable cross-platform high resolution timer function. For each 
platform we select the high resolution timer. You can call the 'ns()' 
function in your file after embedding this. 
*/
#include <stdint.h>
#if defined(__linux)
#  define HAVE_POSIX_TIMER
#  include <time.h>
#  ifdef CLOCK_MONOTONIC
#     define CLOCKID CLOCK_MONOTONIC
#  else
#     define CLOCKID CLOCK_REALTIME
#  endif
#elif defined(__APPLE__)
#  define HAVE_MACH_TIMER
#  include <mach/mach_time.h>
#elif defined(_WIN32)
#  define WIN32_LEAN_AND_MEAN
#  include <windows.h>
#endif
static uint64_t ns() 
static uint64_t is_init = 0;
#if defined(__APPLE__)
    static mach_timebase_info_data_t info;
    if (0 == is_init) 
        mach_timebase_info(&info);
        is_init = 1;
    
    uint64_t now;
    now = mach_absolute_time();
    now *= info.numer;
    now /= info.denom;
    return now;
#elif defined(__linux)
    static struct timespec linux_rate;
    if (0 == is_init) 
        clock_getres(CLOCKID, &linux_rate);
        is_init = 1;
    
    uint64_t now;
    struct timespec spec;
    clock_gettime(CLOCKID, &spec);
    now = spec.tv_sec * 1.0e9 + spec.tv_nsec;
    return now;
#elif defined(_WIN32)
    static LARGE_INTEGER win_frequency;
    if (0 == is_init) 
        QueryPerformanceFrequency(&win_frequency);
        is_init = 1;
    
    LARGE_INTEGER now;
    QueryPerformanceCounter(&now);
    return (uint64_t) ((1e9 * now.QuadPart)  / win_frequency.QuadPart);
#endif

/* ----------------------------------------------------------------------- */-------------------------------- */

【讨论】:

【参考方案13】:

如果项目中使用的是 Qt 框架,最好的解决方案可能是使用 QElapsedTimer。

【讨论】:

【参考方案14】:

在这里聚会迟到了,但我正在使用尚未升级到 c++11 的遗留代码库。我们团队中没有人精通 C++,因此添加像 STL 这样的库被证明是困难的(除了其他人提出的关于部署问题的潜在担忧之外)。我真的需要一个非常简单的跨平台计时器,它可以独立运行,除了基本的标准系统库之外没有任何东西。这是我发现的:

http://www.songho.ca/misc/timer/timer.html

在此处重新发布整个源代码,以免网站停止运行时丢失:

    //////////////////////////////////////////////////////////////////////////////
// Timer.cpp
// =========
// High Resolution Timer.
// This timer is able to measure the elapsed time with 1 micro-second accuracy
// in both Windows, Linux and Unix system 
//
//  AUTHOR: Song Ho Ahn (song.ahn@gmail.com) - http://www.songho.ca/misc/timer/timer.html
// CREATED: 2003-01-13
// UPDATED: 2017-03-30
//
// Copyright (c) 2003 Song Ho Ahn
//////////////////////////////////////////////////////////////////////////////

#include "Timer.h"
#include <stdlib.h>

///////////////////////////////////////////////////////////////////////////////
// constructor
///////////////////////////////////////////////////////////////////////////////
Timer::Timer()

#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceFrequency(&frequency);
    startCount.QuadPart = 0;
    endCount.QuadPart = 0;
#else
    startCount.tv_sec = startCount.tv_usec = 0;
    endCount.tv_sec = endCount.tv_usec = 0;
#endif

    stopped = 0;
    startTimeInMicroSec = 0;
    endTimeInMicroSec = 0;




///////////////////////////////////////////////////////////////////////////////
// distructor
///////////////////////////////////////////////////////////////////////////////
Timer::~Timer()





///////////////////////////////////////////////////////////////////////////////
// start timer.
// startCount will be set at this point.
///////////////////////////////////////////////////////////////////////////////
void Timer::start()

    stopped = 0; // reset stop flag
#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceCounter(&startCount);
#else
    gettimeofday(&startCount, NULL);
#endif




///////////////////////////////////////////////////////////////////////////////
// stop the timer.
// endCount will be set at this point.
///////////////////////////////////////////////////////////////////////////////
void Timer::stop()

    stopped = 1; // set timer stopped flag

#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceCounter(&endCount);
#else
    gettimeofday(&endCount, NULL);
#endif




///////////////////////////////////////////////////////////////////////////////
// compute elapsed time in micro-second resolution.
// other getElapsedTime will call this first, then convert to correspond resolution.
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInMicroSec()

#if defined(WIN32) || defined(_WIN32)
    if(!stopped)
        QueryPerformanceCounter(&endCount);

    startTimeInMicroSec = startCount.QuadPart * (1000000.0 / frequency.QuadPart);
    endTimeInMicroSec = endCount.QuadPart * (1000000.0 / frequency.QuadPart);
#else
    if(!stopped)
        gettimeofday(&endCount, NULL);

    startTimeInMicroSec = (startCount.tv_sec * 1000000.0) + startCount.tv_usec;
    endTimeInMicroSec = (endCount.tv_sec * 1000000.0) + endCount.tv_usec;
#endif

    return endTimeInMicroSec - startTimeInMicroSec;




///////////////////////////////////////////////////////////////////////////////
// divide elapsedTimeInMicroSec by 1000
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInMilliSec()

    return this->getElapsedTimeInMicroSec() * 0.001;




///////////////////////////////////////////////////////////////////////////////
// divide elapsedTimeInMicroSec by 1000000
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInSec()

    return this->getElapsedTimeInMicroSec() * 0.000001;




///////////////////////////////////////////////////////////////////////////////
// same as getElapsedTimeInSec()
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTime()

    return this->getElapsedTimeInSec();

和头文件:

//////////////////////////////////////////////////////////////////////////////
// Timer.h
// =======
// High Resolution Timer.
// This timer is able to measure the elapsed time with 1 micro-second accuracy
// in both Windows, Linux and Unix system 
//
//  AUTHOR: Song Ho Ahn (song.ahn@gmail.com) - http://www.songho.ca/misc/timer/timer.html
// CREATED: 2003-01-13
// UPDATED: 2017-03-30
//
// Copyright (c) 2003 Song Ho Ahn
//////////////////////////////////////////////////////////////////////////////

#ifndef TIMER_H_DEF
#define TIMER_H_DEF

#if defined(WIN32) || defined(_WIN32)   // Windows system specific
#include <windows.h>
#else          // Unix based system specific
#include <sys/time.h>
#endif


class Timer

public:
    Timer();                                    // default constructor
    ~Timer();                                   // default destructor

    void   start();                             // start timer
    void   stop();                              // stop the timer
    double getElapsedTime();                    // get elapsed time in second
    double getElapsedTimeInSec();               // get elapsed time in second (same as getElapsedTime)
    double getElapsedTimeInMilliSec();          // get elapsed time in milli-second
    double getElapsedTimeInMicroSec();          // get elapsed time in micro-second


protected:


private:
    double startTimeInMicroSec;                 // starting time in micro-second
    double endTimeInMicroSec;                   // ending time in micro-second
    int    stopped;                             // stop flag 
#if defined(WIN32) || defined(_WIN32)
    LARGE_INTEGER frequency;                    // ticks per second
    LARGE_INTEGER startCount;                   //
    LARGE_INTEGER endCount;                     //
#else
    timeval startCount;                         //
    timeval endCount;                           //
#endif
;

#endif // TIMER_H_DEF

【讨论】:

以上是关于C++ 跨平台高分辨率计时器的主要内容,如果未能解决你的问题,请参考以下文章

用于定期调用的现代高分辨率计时器

C中的最佳计时方法?

C++两种计时方式(windows平台,毫秒级&微秒级)

C ++ Windows中的高分辨率计时器库? [复制]

实现高分辨率计时器的最佳方法

JavaScript 跨浏览器,高分辨率计时器功能(替换Date()。getTime())。