在性能关键软件(Qt)中使用计时器

Using timers with performance-critical software (Qt)

本文关键字:计时器 Qt 性能 软件      更新时间:2023-10-16

我正在开发一个应用程序,负责通过UDP连接移动和管理机器人。

程序需要:

  • 使用SDL读取操纵杆/用户输入
  • 每20毫秒生成并向机器人发送一个控制数据包(UDP)
  • 接收并解码来自机器人的应答报文(~20 msecs)。这是通过信号/插槽机制实现的,不需要定时器。
  • 接收和处理机器人消息,用于调试。
  • 定期更新UI,让用户了解机器人的状态(例如电池电压)。在大多数情况下,我也使用了Qt的信号/槽机制。
  • 使用看门狗,如果在1秒后没有收到响应,则禁用机器人。当应用程序接收到机器人数据包(~20 msecs)
  • 时,看门狗复位。

目前,我已经实现了上述所有功能。但是,当看门狗被激活或者两个或两个以上的QTimer对象被使用时,应用程序无法正常发送数据包。应用程序通常可以工作,但我不认为它是"生产就绪"的。我已经尝试使用定时器的精度标志(Qt::Precise, Qt::CoarseQt::VeryCoarse),但我仍然遇到问题。

指出:

  • 代码通常组织良好,在代码库中没有"上帝对象"(大多数源文件少于150行,只创建必要的依赖项)。
  • 大多数情况下,我使用QTimer::singleShot()(例如,我只会发送下一个数据包一旦当前数据包已经发送)。

使用计时器的地方:

  • 读取操纵杆输入(~50毫秒,精确计时器)
  • 发送机器人数据包(~ 20msecs,精确定时器)
  • 更新UI的某些方面(~500毫秒,粗定时器)
  • 更新机器人启动后经过的时间(~100毫秒,精确计时器)
  • 实现一个看门狗(如果1000毫秒没有机器人响应,将应用程序和机器人置于安全状态)
  • 注意:看门狗在我们收到机器人的响应包(~20 msecs)时为馈入

对于在性能关键型代码中使用QTimer对象,您有什么建议吗?请注意,我也尝试过使用不同的线程,但它给我带来了更多的问题,因为应用程序不会"同步",从而无法有效地控制我们测试的机器人。

实际上,我似乎低估了Qt的计时器和事件循环性能。在我的系统上,一个事件循环周期的平均时间约为20k纳秒,加上调度排队函数调用的开销,间隔为1毫秒的计时器很少延迟,大多数超时都比一毫秒短几千纳秒。但它是一个高端系统,在嵌入式硬件上可能会更糟糕。

您应该花时间和概要分析您的目标系统和Qt构建,以确定它是否确实可以运行得足够快,并根据这些测量,调整您的时间来补偿系统延迟,以使您的事件更及时地安排。

你一定要让计时器线程尽可能的空闲,因为如果你用IO或者大量的计算阻塞它,你的计时器将不准确。使用专用线程来调度工作,并使用额外的工作线程来完成实际工作。你也可以尝试一下线程优先级。

最坏的情况下,寻找第三方高性能事件循环实现或创建您自己的,也可能是一个更快的信号机制。正如我已经在评论中提到的,Qt的线程间队列信号非常慢,至少与间接函数调用之类的东西相比。

最后但并非最不重要的是,如果您想每N个单位时间执行任务X,那么只有当任务X在您的系统上花费N个或更少的时间时才有可能。您需要考虑每个任务以及并发运行的所有任务。为了获得准确的调度,您应该测量任务X所花费的时间,如果少于它的频率,则在剩余时间内调度下一次执行,否则立即执行。