QTBUG-7618: QTimer consumes too much CPU

    这个Bug是在为公司一个项目编写idle延迟处理机制时发现的,在QT里面定时器类QTimer存在一个CPU高占用的问题,Bug的详细描述和解决方案已经在官方社区有提到:https://bugreports.qt-project.org/browse/QTBUG-7618。

    社员Daniel Kristjansson给出一个临时解决该问题的补丁(qt4,qt5),当然他也提到了最佳的解决方案:But probably the correct approach is to use nanosleep() on POSIX 1999 or later systems and simply sleep a full millisecond on systems 或者 since select is being used to also monitor other things.. pselect() could be used instead to get nanosecond accuracy。

    Daniel Kristjansson提供了示例程序来说明该Bug,我自己也写出QT和GTK+两个版本来对比,使用的CPU是Intel® Core™ i5-2400 CPU @ 3.10GHz × 4,使用的进程CPU信息查看工具是htop,操作系统用Ubuntu 12.04 LTS。

QT: 使用Version 4.8.1

#include <QtGui/QtGui>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QTimer t;
    t.start(1);

    return app.exec();
}

    qt_timer

 

GTK+:使用Version 2.0

#include<gtk/gtk.h>
#include<time.h>

void  TimePorcFunction ()
{
	//printf("ThisTimePorcFunction () \n");
    return;
}
int main(int argc,char * argv[])
{
	gtk_init(&argc,&argv);
	gtk_timeout_add(1, (GtkFunction)TimePorcFunction, NULL);
	gtk_main();
	return 1;
} 

        gtk_timer

 

    对比:QTimer实现的计时器程序qt_timer在timeout间隔为1ms时,几乎100%占满CPU;而GTK+实现的gtk_timer的CPU占用率在2%-3%之间。

    在Bug报告中,Daniel Kristjansson说到“QTimer spends up to the last millisecond of the timer interval busy-waiting, in its attempt to be very accurate”。即是说QTimer内部实现时,计时器最后1ms是使用busy-waiting的方式处理,目的也就是想提高精确度。因此这个Bug的临时解决方案就是在计时器计算最后1ms时,特意加上一个数,利用除法四舍五入(rounding up),使计时器提前结束(timeout),不留下最后1ms。代码示例:

*timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); 

vs

*timeout = (tv.tv_sec * 1000) + ((tv.tv_usec + 999) / 1000);

    虽然临时解决方案不完美,提前结束计时器通常意味着会提前触发某一事件,但是由于QTimer本身并不是高精度计时器,所以即使提前结束计时器的最后1ms也不会造成多大问题。

    最后看下应用Daniel Kristjansson的补丁后,上文提到的QT示例程序表现如何:

    qt_timer_plus_patch

    可以明显地看到此时QTimer实现的计时器qt_timer占用率降低,在3%-4%之间。

    总结:

    1、之所以发现这个Bug是小组老大在跟我一起分析我写的代码时,用valgrind+callgrind以及htop这个小工具分析到程序空闲时仍有计时器消耗较高的CPU,这个是分析程序的好方法;

    2、QT的Bug得先去其官方的bugreports.qt-project.org先找,像这个Bug就有Daniel Kristjansson给出这么好的解析和解决方案;

    3、有时QT的实现可以用其他库做同样的实现来对比,利于比较和发现问题;

    4、QT有社区维护,因此有问题首先到社区寻找帮助。

    最后我将此Bug有关的示例程序和patch放到我个人的github上,地址是:https://github.com/chenjiexin/qtbug7618

你可能感兴趣的:(QTBUG-7618: QTimer consumes too much CPU)