C++ 使用QThreads未正确利用CPU内核

C++ 使用QThreads未正确利用CPU内核,c++,multithreading,qt,qthread,C++,Multithreading,Qt,Qthread,使用:C++(MinGW),Qt4.7.4,Vista(OS),intel core2vPro 我需要以完全相同的方式处理2个大文件。因此,我想从两个单独的线程为两个单独的文件调用处理例程。GUI线程不做任何繁重的事情;只显示一个标签并运行一个事件循环来检查线程终止条件的发出,并相应地退出主应用程序。我希望这两个内核(intel core2)的利用率大致相同,但相反,我从Task Manager中看到,一个内核的利用率很高,而另一个则没有(尽管不是每次运行代码时);另外,处理这两个文件所需的时间

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

我需要以完全相同的方式处理2个大文件。因此,我想从两个单独的线程为两个单独的文件调用处理例程。GUI线程不做任何繁重的事情;只显示一个标签并运行一个事件循环来检查线程终止条件的发出,并相应地退出主应用程序。我希望这两个内核(intel core2)的利用率大致相同,但相反,我从Task Manager中看到,一个内核的利用率很高,而另一个则没有(尽管不是每次运行代码时);另外,处理这两个文件所需的时间远远超过处理一个文件所需的时间(我认为应该相等或稍多,但这几乎等于在非线程应用程序中逐个处理这两个文件)。我可以强制线程使用我指定的核心吗

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

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


一般来说,你最好是按顺序读取文件,然后使用生产者-消费者模型并行处理它们。

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

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

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

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


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

文件在单独的物理硬盘上吗?如果您试图同时读取两个文件,那么每次调度不同的线程时,都必须在它们之间查找,这一部分会淹没你从CPU获得的任何东西。文件大小大致相同吗?@PeteKirkham:只需要看一个HDDi。但是,你能告诉我如何在选择的核心上强制执行线程吗?@ustulation:
qthread
不提供这样的关联API。此外,您几乎不需要将关联设置为单独的核心,因为调度程序将尽可能最好地设置线程到CPU的映射。无论如何,如果您真的需要,请参阅这篇关于使用对pthread库的调用来实现这一点的文章:谢谢..我想我需要坚持顺序处理,因为文件大小不允许先读入RAM,然后再进行处理..另请注意,我从来没有使用过
boost threads
。你能给我一个提示,说明它是否有此功能(核心亲和API),以便我在需要时深入研究它吗?@ustulation:我想你不明白答案。你不需要线程。因此,您也不需要线程关联。如果您需要更高的性能,请购买SSD。@U调试:据我所知,boost也不提供此功能。事实上,高级线程库(boost、tbb、qthread)倾向于避免这种低级机制,因为它会产生可移植性问题。如果在linux上需要显式关联,只需使用经典的pthreads。任何让单个线程耗尽其读/写能力的系统都是不稳定的,或者至少对用户没有响应。除非你走自己的路,否则线程会有效地限制其IOs。您所做的是通过启动两个线程来要求您的程序具有更高的优先级。一切都取决于文件的大小。根据机械硬盘驱动器的经验,如果您希望开销低于10%,则必须一次读取几兆字节。因此,如果这些文件小于,比如说,2M字节,那么您可以完整地读取它们。如果它们更大,那么您可以在文件之间进行循环,以使更多的计算线程保持忙碌。