并行版本的循环不比串行版本快

时间:2010-04-14 10:59:03

标签: performance multithreading parallel-processing boost-thread atomic-values

我正在用C ++编写一个程序来执行特定系统的模拟。对于每个时间步,执行的最大部分是由单个循环占用。幸运的是,这是非常平行的,所以我决定使用Boost Threads并行化它(我在2核机器上运行)。我期望加速接近串行版本的2倍,因为没有锁定。但是我发现根本没有加速。

我按如下方式实现了循环的并行版本:

  • 唤醒两条线(它们被阻挡在屏障上)。
  • 然后每个线程执行以下操作:

    • 以原子方式获取并递增全局计数器。
    • 使用该索引检索粒子。
    • 对该粒子执行计算,将结果存储在单独的数组中
    • 等待工作完成障碍
  • 主线程等待作业完成障碍。

我使用这种方法,因为它应该提供良好的负载平衡(因为每次计算可能需要不同的时间量)。我真的很好奇可能导致这种放缓的原因。我总是读到原子变量很快,但现在我开始怀疑它们是否有性能成本。

如果有人想要寻找什么或任何提示,我会非常感激。我一直在猛烈抨击它一个星期,并且剖析并没有透露太多。

编辑:问题解决了! 我将详细说明我是如何解决这个问题的。我再次使用gprof,但这次编译时没有优化标志(-O3)。然后,分析器立即表明我在函数中花费了不可思议的时间,该函数对每个单独的粒子执行计算:远远超过串行版本。

此功能是虚拟的,可以多态访问。我改变了代码直接访问它,而不是通过vtable和voila'并行版本产生了近2的加速!串行版本的相同更改几乎没有效果。

我不确定为什么会这样,如果有人知道的话会感兴趣!

感谢所有的海报。你们在某种程度上都有所帮助,接受一个答案是非常困难的。

5 个答案:

答案 0 :(得分:2)

  

对该粒子执行计算,将结果存储在单独的数组中

计算有多重?

  • 一般来说,原子计数器可能需要花费数百个时钟周期,这一点非常重要 看到你不仅增加了计数器。
  • 还要试着看看每个线程做了多少工作 - 它们是否合作得好(即在每个周期,每个线程都进行大约一半的粒子)。
  • 尝试将作业细分为更大的块然后单个粒子(比方说100粒子等)。
  • 查看线程以外的工作量。

老实说......看起来你在谈论的是一个错误。

答案 1 :(得分:1)

profiling has not revealed much

目前尚不清楚。我有在HP-UX上分析多线程应用程序的经验,并且他们的分析器表示每个函数运行的时间百分比。因此,如果您的函数中有一个或几个争用点,则应用程序在这些函数中花费的时间会增加。在我的情况下,pthread_mutex_unlock()显着增加。当我更改代码时,它变得更快。

所以你可以在这里发布一个线程和两个/四个线程的相同统计信息。和每次测试中的计算次数。

另外,我建议您(如果可能的话)在锁定互斥锁的全局函数上设置断点。您可能会发现算法中的某个位置偶然会锁定全局互斥锁。

答案 2 :(得分:1)

你的语言有点透露:

  

等待xxx

这可能是你的问题。


另外,再次添加到单个结果队列时会变慢 - 如果可能,您可以仅在处理结束时将结果添加到单个队列中。主线程不应该等待,每次更新后都要检查全局计数器 我将添加您在最后记录的性能计数器,而不是分析。您可能会将它们置于条件编译错误中,因此它们不会添加到您的生产代码中。

答案 3 :(得分:0)

你说剖析没有透露太多,而且(遗憾地)是典型的。

这就是我要做的事情:

  1. 返回单线程。

  2. 使用this profiling technique that works in any language and environment尽可能快地创建单个线程。原因是剖析器(大多数但不是全部)只擅长测量变化,而不是精确定位你应该修复的东西

  3. 然后返回1-thread-per-core,再次执行该过程。如果您发现一个线程或另一个线程在进程间通信上花费了很多时间,那么您需要重新进行操作。

答案 4 :(得分:0)

我可以建议您可以更轻松地找到OpenMP这种并行吗?因为你只是想让循环并行,你真的不想明确地使用线程,这正是OMP真正有效的事情。

无论如何都值得一试。