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

Ami*_*mer 69 c++ cross-platform timer

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

How*_*ant 141

更新旧问题的答案:

在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';
}
Run Code Online (Sandbox Code Playgroud)

示例输出:

74 nanoseconds
Run Code Online (Sandbox Code Playgroud)

"chrono_io"是用这些新类型缓解I/O问题的扩展,可在此处免费获取.

还有一个<chrono>可用于boost 的实现(可能仍然在主干上,不确定它是否已被释放).

更新

这是对Ben在下面的评论的回应,后续调用std::chrono::high_resolution_clock在VS11中需要几毫秒.以下是一个<chrono>兼容的解决方法.但是它只能在英特尔硬件上运行,你需要深入内联汇编(这样做的语法因编译器而异),你必须将机器的时钟速度硬连接到时钟:

#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();
Run Code Online (Sandbox Code Playgroud)

所以它不便携.但是如果你想在自己的英特尔硬件上试验高分辨率时钟,它就不会比这更好.虽然预先警告,但今天的时钟速度可以动态变化(它们实际上不是编译时常量).使用多处理器机器,您甚至可以从不同的处理器获取时间戳.但是,我的硬件实验仍然相当不错.如果你坚持毫秒级分辨率,这可能是一种解决方法.

这个时钟在你的cpu的时钟速度方面有一个持续时间(就像你报告的那样).也就是说,这个时钟每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);
Run Code Online (Sandbox Code Playgroud)

转换将截断cpu周期的分数以形成纳秒.其他舍入模式也是可能的,但这是一个不同的主题.

对我来说,这将返回一个低至18个时钟周期的持续时间,截止为6纳秒.

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

当您将此代码移植到新计算机并忘记更新clock::period到新计算机的速度时,添加此项将节省一些调试时间.所有检查都在编译时或程序启动时完成.所以它不会影响性能clock::now().

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

Jos*_*ley 40

对于C++ 03:

Boost.Timer可能会起作用,但它取决于C函数clock,因此可能没有足够好的分辨率.

Boost.Date_Time包含之前在Stack Overflow上推荐的ptime.看到它的文档上microsec_clock::local_timemicrosec_clock::universal_time,但要注意它告诫说,"Win32系统往往不通过这个API达到微秒级的分辨率."

除了其他功能之外,STLsoft还提供围绕特定于操作系统的API的瘦跨平台(Windows和Linux/Unix)C++包装器.它的性能库有几个类可以满足您的需求.(为了使它跨平台,选择类似于和名称空间performance_counter中存在的类,然后使用与您的平台匹配的任何名称空间.)winstlunixstl

对于C++ 11及更高版本:

std::chrono库内置了此功能.有关详细信息,请参阅@HowardHinnant的回答.

  • 由于这是一个着名的问题/答案,更新可能会很棒.具体来说,这可以使用现代C++特性以标准和可移植的方式实现,如`<chrono>`和`<thread>`?如果可能,怎么样? (6认同)

dcw*_*dcw 6

Matthew WilsonSTLSoft库提供了几种定时器类型,具有一致的接口,因此您可以即插即用.其中包括低成本但低分辨率的定时器,以及高分辨率但成本高的定时器.还有一些用于测量预螺纹时间和测量每个工艺时间,以及测量经过时间的所有时间.

几年前在Dobb博士的文章中有一篇详尽的文章,虽然它只涉及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;
}
Run Code Online (Sandbox Code Playgroud)

HTH


Mal*_*sen 5

StlSoft开源库提供了一个相当不错的计时器在Windows和Linux平台.如果您希望它自己实现,只需看看它们的来源.


小智 5

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

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