2016-06-06 62 views
1

我试图玩std库,特别是线程之一。 这个想法是用多线程和不用多线程来填充矩阵。我有8个内核。C++多线程std库:比预期慢

void fillMatrix(int ID, std::vector<std::vector<double>> & lMatrix,int inThread){ 

    for (int i = 0; i < 10000; ++i){ 
     if (i % inThread == ID){ 
      for (int j = 0; j < 10000; ++j){ 
       lMatrix[i][j] = 123456.0; 
      } 
     } 
    } 

} 


void testMT(int inThread){ 

    std::vector<std::thread> lPool; 
    std::vector<std::vector<double>> lMatrix(10000, std::vector<double>(10000)); 

    for (int i = 0; i < inThread; ++i){ 
     lPool.push_back(std::thread(std::bind(&fillMatrix,i, lMatrix,inThread))); 
    } 

    for (std::thread & t : lPool){ 
     t.join(); 
    } 
} 

的主要代码:

int main(){ 
    const clock_t begin_time1 = clock(); 
    testMT(1); 
    std::cout << float(clock() - begin_time1)/CLOCKS_PER_SEC; 
} 

testMT(1)取2.1秒运行,而testMT(8)开7.032秒。

有什么想法? 谢谢。

+2

我的猜测是分支预测失败。如果通过让外层循环在'ID'开始并通过'inThread'增加来消除'if',会发生什么? –

+0

所以你测量的总CPU时间,在八个线程中会比一个更大。 –

+0

比较1次填充矩阵调用与8次调用。为什么你决定8次呼叫必须快于1? – AnatolyS

回答

4

您的代码没有按照您的要求运行。线程运行后,您会注意到lMatrix的所有元素都没有设置为123456.0。这是因为std::bind将向量复制到自身,然后发送到线程。您需要在绑定调用中使用std::ref(lMatrix)才能正常工作并使用相同的矩阵。

变化

std::bind(&fillMatrix,i, lMatrix,inThread) 

std::bind(&fillMatrix, i, std::ref(lMatrix), inThread) 

你增加执行时间的显著一部分可能因此被复制的开销造成的;每个线程启动时会进行10000 x 10000的分配和复制(然后再进行释放)。

+0

@FredLarson我同意,但是因为它在2-7s的时候在OP的机器上运行,而我在12-18ms运行(他的评论说这个修复“让它快得多”),我不知道所用的850MB内存是更多的税收比预期。 – vu1p3n0x

+0

糟糕,我在测试中遇到了一个错误。 8线程版本仍然比单线程版本慢3倍。 –

5

如果会降低速度,那么您的的性能很差。

在1个线程的情况下,它始终为真,但在8个线程的情况下,它是真的,然后假false ...真。分支预测在这里失败。

将第一个for循环分割/分成每个线程要好得多。像线程1执行矩阵的第一个八分之一,线程2执行第二个八分之一,依此类推。

是这样的:

for(int i = 10000*ID/inThread; i< 10000*(ID+1)/inThread; ++i) 

在ID = 0的情况下,循环会从0到2500,ID = 1将去从2500至5000,等等。

+0

是的,正如弗雷德拉森所说,有一种更有效的方式来做到这一点。然而,我想坚持我的实现,并理解为什么时间计算增加了三倍。谢谢。 – Canardini

+0

@Canardini出于好奇,你能否将这个实现与你的时间相提并论?我怀疑外部循环中的分支预测,或者在这种情况下,缓存未命中可能会产生这样的影响。也许还有其他玩法呢...... – Quinchilion

4

你的线程都在修改附近内存位置的同一个对象。这意味着内存的所有权必须不断地从一个线程跳到另一个线程。

这不是一个明智的使用线程。如果您希望在多个线程都将围绕着同时操纵同一个对象的时候保持性能,那么需要专业知识来处理错误共享等问题。

+0

每个线程在内部循环中写入的内存块大小都是80kb。超越任何缓存行大小。 – Quinchilion

+0

的确如此,但是线程什么都不做,只能修改那些块,每个块可以在缓存线上覆盖前一块和后一块。本质上,线程什么都不做,只是用静态数据填充内存。很难看出多线程如何加快速度 - 一条线程可以使内存饱和。 –

0

从某种意义上说,这是一个非常重要的问题。

  • 多线程代码比单线程代码慢,总是! (最好的情况下,你仍然需要支付启动和停止线程的代价)

  • 并发性使得事情变得更快(有时)。

你的目标是具有并发超过使用线程的成本收益 - 和拥有的资源是如此显著,它“支付”的代码的复杂性。这个例子没有达到这个目标。