5

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

我实现了循环的并行版本,如下所示:

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

    • 原子地获取并增加一个全局计数器。
    • 检索具有该索引的粒子。
    • 对该粒子执行计算,将结果存储在单独的数组中
    • 等待工作完成障碍
  • 主线程等待作业完成屏障。

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

如果有人有一些想法要寻找什么或任何提示,我将不胜感激。我一直在抨击它一个星期,并且分析并没有透露太多。

编辑:问题解决了! 我将详细说明我是如何解决这个问题的。我再次使用 gprof,但这次编译时没有优化标志 (-O3)。立即,分析器表明我在对每个粒子执行计算的函数上花费了令人难以置信的时间:比在串行版本中要多得多。

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

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

感谢所有的海报。你们都在一定程度上有所帮助,很难接受一个答案。

4

5 回答 5

2

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

计算量有多大?

  • 一般来说,原子计数器可能会花费数百个时钟周期,重要的是要看到您不仅要增加计数器。
  • 还要尝试查看每个线程做了多少工作——它们是否合作良好(即在每个循环中,每个线程都进行大约一半的粒子)。
  • 尝试将工作细分为更大的块,然后是单个粒子(比如说 100 个粒子等等)。
  • 查看在线程之外完成了多少工作。

老实说......看起来你在说什么是一个错误。

于 2010-04-14T11:06:47.900 回答
1

profiling has not revealed much

这还不清楚。我有在 HP-UX 上分析多线程应用程序的经验,他们的分析器在那里显示每个函数运行的时间百分比。因此,如果您的函数中有一个或几个争用点,您的应用程序在这些函数上花费的时间就会增加。就我而言,我的pthread_mutex_unlock(). 当我更改代码时,它变得更快。

所以你能在这里发布一个线程和两个/四个线程的相同统计数据吗?以及每次测试中的计算次数。

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

于 2010-04-14T11:24:26.380 回答
1

你的语言有点揭示:

等待xxx

这可能是你的问题。


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

于 2010-04-14T11:54:54.980 回答
0

你说分析没有透露太多,这(可悲的是)是典型的。

这是我要做的:

  1. 回到单线程。

  2. 通过使用这种适用于任何语言和环境的分析技术,使该单线程尽可能快。原因是分析器(大多数但不是全部)只擅长测量变化,而不是指出你应该修复什么

  3. 然后回到每核 1 个线程,并再次执行该过程。如果您发现一个线程或另一个线程在进程间通信上花费了很多时间,那么您需要重新处理它。

于 2010-04-14T11:30:24.033 回答
0

我能否建议您可能会发现 OpenMP 更容易实现这种并行性?由于您只想使循环并行,因此您并不想明确地使用线程,而这正是 OMP 可以真正有效的事情。

无论如何都值得一试。

于 2010-04-14T12:04:24.613 回答