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次。有没有更有效的方法来执行此任务?我不确定一次启用这么多线程是否会影响性能。我听说过线程池库,但是不确定是否会为我带来任何好处。任何帮助表示赞赏。 非常感谢你。     

解决方法

除非目标计算机中具有400多个处理器内核,否则产生400个CPU绑定线程绝对没有任何好处。 不可能毫无把握地告诉您如何更好地分配工作负载,而不知道您正在执行哪种计算以及关于哪种数据。 从发布的内容来看,在黑暗中拍摄照片时,第一个方法是使用
N
个线程(请参阅下文),并在其中划分400个对象,以便每个线程负责处理大约
400/N
个对象。每个线程可以循环300次,并且在每次迭代中,它可以处理其分配的每个对象。
N
是任意数字;实际上,我建议尝试使用其他值并比较性能结果。但是,除非您的线程正在执行I / O或其他会浪费时间在非计算操作上的操作,否则
N
不应大于计算机中处理器内核的数量(尝试一下并迅速观察性能下降)。 编辑:根据正在进行的讨论,建议使用对象队列,从中可以轻松弹出每个
N
线程的对象,以准备进行更多工作。队列当然需要是线程安全的。为了获得最佳性能,应实施无锁队列。这里有好纸。应该通过一次完全填充队列,因此只需要线程安全的读取来简化实现。     ,拥有比实际执行引擎(CPU或核心或正在使用的任何东西-我在这里仅称其为CPU)多的线程的唯一有益方法是,是否某些线程实际上正在等待其他资源比那些CPU。 如果线程是CPU绑定的,则理想数量等于可用的CPU数量。如果许多线程正在等待文件I / O或数据库访问,网络流量或OS事件(等等),那么可能有几百个线程可以。但是,就您而言,情况似乎并非如此。 线程池实际上是一种避免在效率相对较低的情况下连续创建和销毁线程的方法。例如,如果启动一个线程需要十秒钟,而每个线程仅完成一秒钟的工作,那么线程池将是理想的选择。 鉴于您可能会将线程数减少到实质上少于四百个(例如大约两个或四个),这反过来又会增加每个线程的工作量,因此可能不需要线程池。但是同样,它取决于线程将要完成的工作量及其启动成本。 为简单起见,我将从非池版本开始,仅考虑在存在严重性能问题时进行更改。否则,您可能会为自己付出额外的工作,而没有真正的好处。 您仍然可以将工作划分为四百个单元,但是最好的方法是简单地将它们排队,并让每个线程在准备好处理一个项目时从队列中取出一个项目。这样,工作可以在CPU之间自动平衡。如果出于某种奇怪的原因,CPU 1的运行速度是其他CPU的两倍,它将自动获得两倍的工作量。 这比您想像的要重要得多,仅因为几乎可以肯定的是,CPU也将做其他事情-它们不太可能完全专注于完成这项工作。     ,对于计算量大的任务,数百个线程听起来像是一个问题。程序可能花费更多时间进行上下文切换而不是处理。尝试使用N个线程(其中N是您计算机中的内核数)并将工作分块成更大的单元。     ,对于计算密集型工作,您将受到拥有的内核数量的限制。因此,建议使用与内核一样多的线程。 将工作划分为您拥有的核心数量,并创建相同数量的线程并运行它。 如果所有工作项都是独立的,则只需将其分成相等大小的组。如果工作项之间存在依赖关系(item1需要item1的结果),那么您需要根据该依赖关系分为一些有意义的事情。     ,在单处理器计算机上,如果您所做的全部是由于上下文切换而导致的计算,则多线程处理的速度可能比单线程处理的速度要慢。 通常,如果某些线程正在等待某些外围设备,则多线程方法可能会为您的应用程序提供一些灵活性。 就您而言-CPU密集型任务-我怀疑多线程方法能否为您的应用程序带来性能。     ,首先,超过最大并发线程数是浪费。 1个具有超线程或SMT的内核,或者任何芯片制造商想要称呼的内核都具有2个或更多并行线程。您必须确定您的内核可以处理多少个并行线程,并将其乘以内核数。不需要做更多的线程。您有400个线程。在任何时候,可能有396人在睡觉。 不必担心高速缓存行对齐,您只需担心\“ locality \”。当您遍历大于L2缓存的数据时,每次内存访问都是从慢速内存访问一直到RAM的访问。如果您旋转小于L2缓存的数据,则所有内存访问都在L2缓存中,这快100倍。另外,如果所有数据访问均为慢速访问,则cpu上的所有执行线程都将停止。 SMT之所以起作用,是因为(通常)一个线程停滞等待ram,因此CPU可以执行另一个线程。如果您做错了足够的事情并使所有线程停止运行,则说明您基本上已经禁用了SMT。您现在没有并发线程。 因此...如果您的数据集大于L2缓存,则需要“剥离地雷”。将计算分为足够小的部分以适合L2缓存。例如,如果您有一个矩阵,则将矩阵划分为n x m个正方形,这些正方形可以放入L2高速缓存中,并让正确的线程数在上面工作。完成剥离后,移至下一个,依此类推。如果正确执行此操作,您的代码可能会快100倍。 增加局部性的另一种方法是缩小数据。使数据尽可能小。数据越小,它在L2缓存中保留的时间就越多。     

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...