c++ - 如何降低高分辨率(10 微秒)精确计时器的 CPU 使用率?
问题描述
我正在使用 qt5 和 c++ 在 Windows 10 中为一些复杂的通信应用程序编写一个计时器。我想以微秒分辨率使用最多 3% 的 CPU。
最初我在这个应用程序中使用了 qTimer (qt5)。低 CPU 使用率和开发人员友好的界面很好。但这并不像我需要的那样精确。它只需要毫秒作为参数,但我需要微秒。在许多现实世界的情况下,比如 CPU 负载过重,计时器的精度并不等于这个分辨率。有时计时器会在 1 毫秒触发,有时会在 15 毫秒触发。你可以在图片中看到这个问题:
我搜索了几天的解决方案。但最后我发现 Windows 是一个非实时操作系统 (RTOS),并没有提供高分辨率和精确的计时器。
为此,我编写了自己的带有 CPU 轮询的高分辨率精确计时器。我开发了一个在单独线程中工作的单例类。它以 10 微秒的分辨率工作。
但它正在消耗 CPU 中的一个逻辑核心。相当于锐龙 2700 的 6.25%。
对于我的应用程序,这种 CPU 使用率是不可接受的。我怎样才能在不放弃高分辨率的情况下减少这种 CPU 使用率?
这是完成这项工作的代码:
void CsPreciseTimerThread::run()
{
while (true)
{
QMutexLocker locker(&mMutex);
for (int i=0;i<mTimerList.size();i++)
{
CsPreciseTimerMiddleLayer* timer = mTimerList[i];
int interval = timer->getInterval();
if ( (timer->isActive() == true&&timer->remainingTime()<0))
{
timer->emitTimeout();
timer->resetTime();
}
}
}
}
我试图降低计时器线程的优先级。我用了这条线:
QThread::start(QThread::Priority::LowestPriority);
和这个:
QThread::start(QThread::Priority::IdlePriority);
这种变化使计时器不那么精确,但 CPU 使用率并没有减少。
之后,我尝试强制当前线程在循环中休眠几微秒。
QThread::usleep(15);
正如您可能猜到的那样,睡眠功能确实搞砸了准确性。有时计时器的睡眠时间比预期的要长,例如 10 毫秒或 15 毫秒。
解决方案
我将直接引用 Windows API 而不是 Qt 抽象。
我不认为你想降低你的线程优先级,我认为你想提高你的线程优先级并在轮询之间使用最少的睡眠来平衡延迟和 CPU 开销。
两个想法:
在 Windows Vista 中,他们专门引入了多媒体类调度程序服务,以便他们可以将 Windows 音频组件移出内核模式并在用户模式下运行,而不会影响专业音频工具。这可能会对您有所帮助 - 它不是精确的“实时”保证,但它适用于低延迟操作。
采用经典方式 - 将您的进程和线程优先级提高到高或关键,同时使用几毫秒的合理睡眠语句。也就是说,将您的线程优先级提高到
THREAD_PRIORITY_TIME_CRITICAL
. 然后在循环完成后做一个很小的睡眠。for
此睡眠量应在 0..10 毫秒之间。需要进行一些实验,但在下一次预期超时之前,我的睡眠时间不会超过一半,最长为 10 毫秒。当您在计时器的 N 微秒内时,您可能只需要旋转而不是屈服。需要进行一些实验。您还可以尝试将您的流程优先级提高到REALTIME_PRIORITY_CLASS
.
小心- 少数处于这些较高优先级且未休眠的失控进程和线程可能会锁定系统。
推荐阅读
- python - Kivy Recycleview updates from button but not from app build method
- vue.js - Sort Vue data table by selection
- python - 为什么我收到错误消息:无法获取数据库的路由表
- android - 如何在flutter的firestore中使用uid更新用户数据
- arrays - sscanf 重复字符后的值
- awk - 针对大数据优化 Bash 脚本
- xamarin.forms - 如何以 xamarin 形式检测设备的大小
- c# - Kestrel Port:0,如何检测自动选择的端口
- fpga - 将简单反转(非函数)应用于 OBUFDS
- angular - 错误:将 Angular 8 转换为 Angular Universal 时“无法读取未定义的属性‘种类’”