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

Posted

技术标签:

【中文标题】实现高分辨率计时器的最佳方法【英文标题】:Best way to implement a high resolution timer 【发布时间】:2019-09-16 07:59:19 【问题描述】:

在 C++11 中是什么,该计时器在循环中连续检查时间,并在经过某个时间点后执行一些代码?例如检查从上午 9 点开始循环的时间,并在上午 11 点准确执行一些代码。我要求时间准确(即上午 9 点后不超过 1 微秒)。

我将在 Linux CentOS 7.3 上实现这个程序,并且在专用 CPU 资源来执行这个任务方面没有问题。

【问题讨论】:

@Aconcagua 是的。在这种情况下,有什么建议吗? std::chrono::high_resolution_clock:'表示实现提供的最小滴答周期的时钟' 循环运行对我来说似乎很可疑,但是,如果您需要如此高的精度。至少,您需要确保没有其他进程在同一内核上运行,以避免线程之间的上下文切换。可能,基于中断的解决方案更有可能达到所需的精度。 auto targetTime = std::chrono::high_resolution_clock::now(); /* now get exact time from your atomic clock! With that, calculate exact distance to 11:00 */ targetTime += duration; while(targetTime - std::chrono::high_resolution_clock::now() > 0) /* wait */ (正时差的计算已经简化!)。 1us!哇!好的,循环等待是没有解决办法的。这是实时调度程序的要求。所以你应该考虑在这种情况下使用 RT 内核。但是这样的场景有什么意义。通常,此类事情在板载同步晶体的嵌入式设备上运行。在普通 PC 上保持如此精确的操作系统时钟对我来说听起来是不可能的。你有没有检查过你的 RTC 在你的系统上有多少时间偏差?顺便说一句:你如何与真实的地球时间同步?使用 ntp 协议并没有提供这样的精度! 【参考方案1】:

您可以使用例如,而不是手动实现此功能systemd.timer。确保指定the desired accuracy,它显然可以精确到 1us。

【讨论】:

是否可以在 c++ 程序中包含 systemd.timer?由于它是更大程序的一部分,我无法将代码分离到可以放在 systemd.timer 上的程序中。 @L.Koh:一个 systemd.timer 启动一个 systemd.service。服务启动了一个程序,但你已经运行了。 @L.Koh 是的:如果您的 C++ 程序使用 dbus,您还可以使用 systemd 动态创建和注册计时器。【参考方案2】:

一个在循环中不断检查时间的高分辨率计时器,

首先,您确实想循环不断地检查时间;这效率极低,根本没有必要。

...经过某个时间点后执行一些代码?

好的,因此您希望在未来的给定时间尽可能准确地运行一些代码。

最简单的方法是简单地启动一个后台线程,计算距离目标时间多长时间(以所需的分辨率),然后让线程在该时间段内休眠。当您的线程唤醒时,它会执行实际任务。这应该足以满足绝大多数需求。

std::chrono 库提供的调用让这一切变得简单:

System clock in std::chrono High resolution clock in std::chrono

这是一个 sn-p 代码,它使用系统时钟执行您想要的操作(这使得设置挂钟时间更容易):

// c++ --std=c++11 ans.cpp -o ans

#include <thread>
#include <iostream>
#include <iomanip>

// do some busy work
int work(int count)

    int sum = 0;
    for (unsigned i = 0; i < count; i++)
    
        sum += i;
        
    return sum;


std::chrono::system_clock::time_point make_scheduled_time (int yyyy, int mm, int dd, int HH, int MM, int SS)

    tm datetime = tm;

    datetime.tm_year = yyyy - 1900; // Year since 1900
    datetime.tm_mon = mm - 1; // Month since January
    datetime.tm_mday = dd; // Day of the month [1-31]
    datetime.tm_hour = HH; // Hour of the day [00-23]
    datetime.tm_min = MM;
    datetime.tm_sec = SS;

    time_t ttime_t = mktime(&datetime);

    std::chrono::system_clock::time_point scheduled = std::chrono::system_clock::from_time_t(ttime_t);

    return scheduled;


void do_work_at_scheduled_time()

    using period = std::chrono::system_clock::period;

    auto sched_start = make_scheduled_time(2019, 9, 17,  // date
                                           00, 14, 00);  // time

    // Wait until the scheduled time to actually do the work
    std::this_thread::sleep_until(sched_start);

    // Figoure out how close to scheduled time we actually awoke
    auto actual_start = std::chrono::system_clock::now();
    auto start_delta = actual_start - sched_start;
    float delta_ms = float(start_delta.count())*period::num/period::den * 1e3f;

    std::cout << "worker: awoken within " << delta_ms << " ms" << std::endl;

    // Now do some actual work!
    int sum = work(12345);


int main()

    std::thread worker(do_work_at_scheduled_time);

    worker.join();

    return 0;

在我的笔记本电脑上,典型的延迟约为 2-3 毫秒。如果您使用high_resolution_clock,您应该能够获得更好的结果。

您还可以使用其他 API,例如 Boost,您可以在其中 use ASIO to implement high res timeout。

我要求时间准确(即上午 9 点后不超过 1 微秒)。

真的需要精确到微秒吗?考虑到在此分辨率下,您还需要考虑各种其他因素,包括系统负载、延迟、时钟抖动等。您的代码可以在接近那个时间开始执行,但这只是问题的一部分。

【讨论】:

谢谢@gavinb。这很有帮助。我之前也探索过 struct tm,但问题似乎是无法将 tm 定义到至少毫秒?如果我错了,请纠正我。 @L.Koh struct tm 仅用于定义预定的挂钟时间(以秒为单位),然后将其转换为 std::chrono::time_point,这要准确得多(至少 ms或更好,具体取决于操作系统/平台)。因此,当计算睡眠时间的时间增量时,它将与std::chrono::system_clock::period 一样精确,在我的机器上是微秒。但正如我所提到的,有各种各样的延迟和影响性能的因素。【参考方案3】:

我的建议是使用timer_create()。这使您可以在给定时间收到信号通知。然后您可以在signal handler 中实施您的操作。

在任何情况下,您都应该知道,准确度当然取决于系统时钟的准确度。

【讨论】:

以上是关于实现高分辨率计时器的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

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

C中的最佳计时方法?

Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现

Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现

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

C中的跨平台高分辨率计时器?