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

时间:2011-04-13 07:04:47

标签: c++ multithreading performance threadpool

我目前用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次。有没有更有效的方法来执行此任务?我不确定一次有多少活动线程可能会影响性能。我听说过线程池库,但我不确定它是否会给我带来任何好处。任何帮助表示赞赏。

非常感谢。

6 个答案:

答案 0 :(得分:15)

除非您的目标计算机中有400多个处理器核心,否则产生400个CPU绑定线程绝对没有任何好处。

如果不知道您正在执行什么类型的计算以及使用何种数据,就无法确切地告诉您如何更好地分配工作量。

作为黑暗中的镜头,根据你发布的内容判断,第一次尝试是使用N个线程(见下文),然后将400个对象分开,以便每个线程负责处理大约400/N个对象。每个线程可以循环300次,并且在每次迭代时它都可以处理每个已分配的对象。

N是一个任意数字;事实上,我建议尝试不同的值并比较性能结果。但是,除非您的线程正在执行I / O或其他浪费时间阻塞非计算操作的操作,N不应大于计算机中处理器核心的数量(尝试并观察您的性能迅速放弃)。

编辑:根据正在进行的讨论,建议使用对象的队列,每个N个线程都可以从中弹出,因为它们已准备好进行更多工作。队列当然需要是线程安全的。为获得最佳性能,应实现无锁队列。有一篇好文章here。您应该通过一次完全填充队列来简化实现,因此只需要线程安全读取。

答案 1 :(得分:3)

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

答案 2 :(得分:3)

拥有更多线程比实际执行引擎(CPU或核心或正在使用的任何东西 - 我只是在这里称之为CPU)的唯一方法是,如果某些线程实际上正在等待这些CPU以外的资源。

如果线程受CPU限制,则理想数量等于您可用的CPU数量。如果许多线程正在等待文件I / O或数据库访问或网络流量或OS事件(等等),那么几百个可能没问题。但是,在你的情况下,似乎并非如此。

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

鉴于您可能会将线程数减少到大约不到四百(大约两四个),这反过来会增加每个线程所做的工作,可能不需要线程池。但同样,这取决于线程将与其启动成本相比完成的工作量。

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

您仍然可以将您的工作分成四百个单位,但最好的方法是简单地将它们排队,让每个线程在准备好处理项目时从队列中提取项目。这样,工作在CPU之间自动平衡。如果由于某些奇怪的原因,CPU 1的运行速度是其他CPU的两倍,它将自动获得两倍的工作量。

这比你想象的更重要,仅仅因为它几乎可以肯定CPU也会做其他的东西 - 他们不太可能完全致力于这项工作。 / p>

答案 3 :(得分:2)

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

将工作划分为您拥有的核心数,并创建相同数量的线程并运行它。

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

答案 4 :(得分:1)

在单处理器计算机上,使用多线程比使用单线程更慢如果,由于context switch,您所做的只是计算。

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

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

答案 5 :(得分:1)

首先,超过simultaneous threads的最大数量是浪费。 1个具有超线程或SMT的核心或者芯片制造商想要称之为具有2个或更多同时线程的核心。您必须弄清楚核心可以处理的并发线程数,并将其乘以核心数。不需要做更多的线程。你有400个线程。在任何时候,大概有396人睡着了。

您不必担心缓存行对齐,而是需要担心“locality”。当您旋转大于L2缓存的数据时,每次内存访问都是慢速内存访问,一直到RAM 。如果您旋转小于L2缓存的数据,则所有内存访问都在 L2缓存中,速度快〜100倍。此外,如果所有数据访问都是慢速的,那么cpu上的所有执行线程都将被停止。 SMT只能工作,因为通常一个线程停止等待ram,因此CPU可以执行另一个线程。如果你做错了并且停止所有线程,那么你基本上禁用了SMT。你现在没有同步线程。

所以......如果您的数据集大于L2缓存,则需要“剥离我的”。将计算分解为足够小的部分以适合L2缓存。例如,如果你有一个矩阵,那么将矩阵划分为n x m个正方形,这些正方形可以放入L2缓存中,让正确的#个线程对其起作用。完成该条带后,移动到下一个,依此类推。如果你这样做,你的代码可能会快100倍。

增加位置的另一种方法是缩小数据。使数据尽可能小。数据越小,它在L2缓存中的停留时间就越多。

相关问题