使用 QThreads 未正确使用 CPU 内核

Posted

技术标签:

【中文标题】使用 QThreads 未正确使用 CPU 内核【英文标题】:CPU Cores not Utilized properly using QThreads 【发布时间】:2012-04-09 23:44:56 【问题描述】:

使用:C++ (MinGW)Qt4.7.4Vista (OS)intel core2vPro

我需要以完全相同的方式处理 2 个大文件。所以我想从 2 个单独的线程为 2 个单独的文件调用处理例程。 GUI 线程不做任何繁重的工作;只显示一个标签并运行一个事件循环来检查线程终止条件的发射并相应地退出主应用程序。我希望这可以在某种程度上平等地利用两个内核(intel core2),但相反,我从任务管理器中看到其中一个内核得到了高度利用,而另一个则没有(尽管不是每次我运行代码时);处理这两个文件的时间也比处理一个文件的时间长得多(我认为它应该相等或多一点,但这几乎等于在非线程中一个接一个地处理两个文件应用)。我可以以某种方式强制线程使用我指定的内核吗?

QThread* ptrThread1=new QThread;
QThread* ptrThread2=new QThread;
ProcessTimeConsuming* ptrPTC1=new ProcessTimeConsuming();
ProcessTimeConsuming* ptrPTC2=new ProcessTimeConsuming();

ptrPTC1->moveToThread(ptrThread1);
ptrPTC2->moveToThread(ptrThread2);

//make connections to specify what to do when processing ends, threads terminate etc
//display some label to give an idea that the code is in execution

ptrThread1->start();
ptrThread2->start(); //i want this thread to be executed in the core other than the one used above

ptrQApplication->exec(); //GUI event loop for label display and signal-slot monitoring

【问题讨论】:

文件是否在单独的物理硬盘上?如果您尝试使用 rust 来一次读取两个文件,那么每次安排不同的线程时,您都必须在它们之间进行查找,而这部分将淹没您可能从 CPU 获得的任何东西。 文件大小是否大致相等? @PeteKirkham:只有 1 个硬盘 【参考方案1】:

对于机械硬盘,您需要明确控制顺序读取所用时间与查找所用时间的比率。执行此操作的规范方法是在 m+min(n, QThread::idealThreadCount()) 线程上运行 n+m 个对象。这里,m是文件所在的硬盘数,n是文件数。

每个 m 对象都以循环方式从给定的硬盘驱动器中读取文件。每次读取都必须足够大。在现代硬盘驱动器上,让我们预算 70Mbytes/s 的带宽(您可以对实际值进行基准测试),5ms 用于寻道。要浪费最多 10% 的带宽,每秒只有 100ms 或 100ms/(5ms/seek)=20 次搜索。因此,在读取下一个文件之前,您必须从每个文件中读取至少 70Mbytes/(20seeks+1)=3.3 MB。该线程用文件数据填充缓冲区,然后缓冲区向附加到缓冲区另一侧的相关计算对象发出信号。当缓冲区繁忙时,您只需跳过从给定文件中读取,直到缓冲区再次可用。

其他 n 个对象是计算对象,它们根据来自缓冲区的信号执行计算,该信号指示缓冲区已满。一旦不再需要缓冲区数据,缓冲区就会“重置”,以便文件读取器可以重新填充它。

所有阅读器对象都需要自己的线程。计算对象可以以循环方式分布在它们自己的线程中,使得线程之间的对象都在+1、-0以内。

【讨论】:

【参考方案2】:

我认为我的经验数据可能对本次讨论有所帮助。我有一个包含 980 个 txt 文件的目录,我想阅读这些文件。在 Qt/C++ 框架中并在 Intel i5 四核上运行,我创建了一个 GUI 应用程序并添加了一个类工作者来读取给定路径的文件。我将工人推入一个线程,然后在每次运行时重复添加一个额外的线程。我用 1 个线程计时了大约 13 分钟,用 2 个线程计时了 9 分钟,用 3 个线程计时了 8 分钟。所以,在我的情况下,它有一些好处,但它很快就降级了。

【讨论】:

任何让单个线程耗尽其读/写能力的系统都将是不稳定的——或者至少对用户没有响应。除非您特意为之,否则线程实际上会限制其 IO。您所做的是通过启动两个线程来要求您的程序具有更高的优先级。 一切都取决于文件的大小。作为机械硬盘驱动器的经验法则,如果您希望开销低于 10%,则必须一次读取几兆字节。因此,如果文件小于 2Mbytes,您可以完整地阅读它们。如果它们更大,那么您可以在文件之间循环以保持更多计算线程忙碌。【参考方案3】:

经常从单个机械磁盘并行读取(可能在您的情况下)不会产生任何性能提升,因为磁盘的机械头每次都需要旋转以寻找下一个读取位置,从而有效地进行读取顺序的。更糟糕的是,如果很多线程都在尝试读取,则性能甚至可能相对于顺序版本降低,因为磁盘磁头会反弹到磁盘的不同位置,因此每次都需要从中断的地方旋转回来。

通常,您最好的办法就是按顺序读取文件,然后使用生产者-消费者模型并行处理它们。

【讨论】:

我明白了..你能告诉我如何在所选核心上强制线程吗? @ustulation: qthread 不提供这样的关联 API。此外,您几乎不需要将关联设置为单独的内核,因为调度程序会尽可能地设置线程到 CPU 的映射。无论如何,如果您真的需要,请参阅这篇关于使用调用 pthread 库来实现此目的的帖子:qt-project.org/faq/answer/… 谢谢..我想我需要坚持顺序处理,因为文件大小对于读入 RAM 然后进行处理是禁止的..在单独的说明中,我从未使用过 boost threads..could你给我提示一下它是否有这个功能(核心 Affinity API),以便我在需要时深入研究它? @ustulation:我认为您没有理解答案。 您不需要线程。因此,您也不需要线程亲和性。如果您需要更高的性能,请购买 SSD。 @ustulation:据我所知,boost 也不提供此功能。事实上,高级线程库(boost、tbb、qthread)倾向于避免这种低级机制,因为它会产生可移植性问题。如果您需要在 linux 上显式关联,只需使用经典 pthread。

以上是关于使用 QThreads 未正确使用 CPU 内核的主要内容,如果未能解决你的问题,请参考以下文章

带参数传递的 QThreads

未安装 AMD 处理器 Hax 内核

Linux主机CPU使用率100%,内核态占60%

Spacy,在 python 中的大型数据集上使用 nlp.pipe,多处理导致进程进入睡眠状态。如何正确使用所有 CPU 内核?

内核作为动态链接器范例?

如何让 QThreads 在控制台 PySide 程序中工作?