c++中的大量线程和效率



我最近用c++写了一个程序,有时使用超过300个线程。在我的程序中,我有一个结构体数组,数组的长度等于线程的数量。假设我有400个结构体,因此有400个线程。

在for循环的一次迭代中,我对400个结构体中的每一个都应用了一个函数,并且这个函数在线程中执行。因此,我有400个线程并发运行。(我正在使用boost线程库)。

我试着给出我的代码看起来像什么分解(它不是实际的代码):

struct my_struct{
  // Structure's members
};
std::vector<my_struct> my_vec;
void my_fun(my_struct* my_str){
// Operations on my_str
}
int main(){
  std::vector<boost::thread> thr(400);
  for (int k = 0; k < 300; k++){
    for (int i = 0; i < 400; i++){
      thr.at(i) = boost::thread(my_fun, &my_vec.at(i));
      }
    }
    for (int m = 0; m < M; m++){
      thr.at(m).join();
    }
  }
}

我使用的函数是计算密集型的,从上面的代码来看,我使用了400个线程来进行计算,并且执行了300次。有更有效的方法来完成这项任务吗?我不确定一次活动这么多线程是否会影响性能。我听说过线程池库,但我不确定它是否会给我带来任何好处。如有任何帮助,不胜感激。

Thank You Very Much。

产生400个cpu绑定线程绝对没有好处,除非您的目标机器中有400多个处理器内核。

如果不知道您正在执行何种计算,以及在何种数据上执行计算,则不可能确切地告诉您如何更好地分配工作负载。

作为一个黑暗的镜头,从你所发布的判断,第一次尝试将是使用N线程(见下文),并将你的400个对象分配给它们,以便每个线程负责处理大约400/N对象。每个线程可以循环300次,在每次迭代中,它可以处理每个分配给它的对象。

N为任意数;实际上,我建议尝试不同的值并比较性能结果。但是,除非您的线程正在执行I/O或其他将时间浪费在阻塞非计算性操作上的操作,否则N应该不大于您的机器中的处理器核数(尝试一下,您会看到您的性能迅速下降)。

编辑:根据正在进行的讨论,建议使用一个对象队列,其中每个N线程可以在准备好进行更多工作时简单地弹出。这个队列当然需要是线程安全的。为了获得最佳性能,应该实现无锁队列。这里有一份好报纸。实现应该简化,因为您只需要一次完全填充队列,因此只需要线程安全的读取。

拥有比实际执行引擎(cpu或内核或正在使用的任何东西)更多的线程的唯一有益的方法是,如果一些线程实际上正在等待cpu以外的资源。

如果线程是cpu限制的,理想的数量等于您可用的cpu数量。如果许多线程都在等待文件I/O、数据库访问、网络流量或操作系统事件(等等),那么几百个线程就可以了。但是,就你方而言,情况似乎并非如此。

线程池实际上是一种避免在相对低效的情况下不断创建和销毁线程的方法。例如,如果启动一个线程需要10秒,而每个线程只做一秒钟的工作,那么线程池将是理想的选择。

假设您可能将线程的数量减少到少于400个(例如大约两个或四个),这将反过来增加每个线程所完成的工作,因此可能不需要线程池。但同样,这取决于线程将完成的工作量与它们的启动成本的比较。

为了简单起见,我将从非池版本开始,只有在出现严重的性能问题时才考虑更改。否则,你可能会给自己额外的工作,而没有真正的好处。

你仍然可以把你的工作分成400个单元,但最好的方法是简单地把它们排队,让每个线程在准备好处理一个项目时从队列中取出一个项目。这样,工作就会在cpu之间自动平衡。如果由于某些奇怪的原因,1号CPU的运行速度是其他CPU的两倍,那么它将自动获得两倍的工作负载。

这比你想象的更重要,因为cpu几乎肯定会做其他的事情——它们不可能完全专注于做这项工作。

数百个线程听起来像是计算成本高的任务的问题。程序很可能花更多的时间在上下文切换上,而不是在上下文处理上。尝试使用N个线程(其中N是机器中的核心数)并将工作分成更大的单元。

对于计算密集型工作,您将受到您拥有的内核数量的限制。因此,建议使用尽可能多的线程。

将工作划分为您拥有的内核数量并创建相同数量的线程并运行此

如果所有的工作项都是独立的,您只需将它们分成大小相等的组。如果工作项之间存在依赖关系(item1的结果是item2所需要的),那么您需要根据依赖关系划分一些有意义的内容。

首先,超过并发线程的最大数量是一种浪费。一个带有超线程或SMT或任何芯片制造商想叫它的核有两个或更多的并发线程。您必须计算出您的核心可以处理多少并发线程,并将其乘以核心数量。不需要做更多的线程。你有400个线程。在任何时候,大约有396人在睡觉。

比起担心缓存行对齐,你需要担心的是"locality"。当您旋转处理比L2缓存大的数据时,每次内存访问都是慢速内存访问,一直到RAM。如果您旋转小于L2缓存的数据,则所有内存访问都在L2缓存中,这比快100倍。此外,如果所有的数据访问都是缓慢的,那么cpu上的所有执行线程都将停止。SMT之所以有效,是因为通常情况下,一个线程会暂停等待ram,这样CPU就可以执行另一个线程。如果您做错了很多事情并使所有线程停滞,那么您基本上已经禁用了SMT。现在没有并发线程了。

所以…如果你的数据集大于二级缓存,你需要"strip mine"。将计算分解成足够小的部分,以适应二级缓存。例如,如果您有一个矩阵,那么将矩阵分成n x m平方,可以放入二级缓存,并让正确的线程数在其上工作。当这条线完成后,移动到下一条,依此类推。如果你做得对,你的代码可能会快100倍。

另一种增加局部性的方法是收缩数据。使数据尽可能小。数据越小,它留在二级缓存中的时间就越多。

在单处理器计算机上,如果由于上下文切换,您所做的所有工作都是计算,那么使用多线程很可能比使用单线程慢。

通常,如果一些线程正在等待一些外围设备,那么多线程方法可能会为应用程序提供一些灵活性。

在你的情况下- CPU密集型任务-我怀疑多线程方法会给你的应用程序带来性能

相关内容

  • 没有找到相关文章

最新更新