并行代码的可扩展性不好(Parallel code bad scalability)

2019-08-01 09:07发布

最近我一直在分析我的并行计算实际上是如何在16核处理器加速。 与一般的公式,我得出的结论 - 更多的线程,你每个核心有你得到的少速度 - 是尴尬的我。 这里是我的CPU负载和处理速度的图表:

所以,你可以看到,处理器的负载增加,但增加的速度慢得多。 我想知道为什么这样的效果发生,以及如何获得的不可扩展的行为的原因。 我已经取得了一定要使用服务器GC模式 。 我确信,我只要代码所做的无非是合适的并行代码

  • 从RAM加载数据(服务器具有96 GB的内存,交换文件不应该被打)
  • 不执行复杂的计算
  • 在RAM存储数据

我已经仔细异型我的应用程序并没有发现瓶颈-看起来像每一个操作变得线程数量的增长速度较慢。

我卡住了,这有什么错我的情况?

我使用的.Net 4任务并行库。

Answer 1:

到线性可扩展性的关键 - 在一到两个核心去的背景下双打的吞吐量 - 是使用共享资源,尽可能少。 这意味着:

  • 不要使用超线程(因为两个线程共享相同的核心资源)
  • 每个线程绑定到特定的芯(否则操作系统将耍弄芯之间的线程)
  • 不是有核心不使用多个线程(操作系统将进出交换)
  • 留核自己的缓存里 - 时下L1&L2高速缓存
  • 不要冒险进入L3高速缓存或内存,除非绝对必要,
  • 最小化/节约临界区/同步使用

如果你已经走到这一步,你可能已经成型和手工调整你的代码了。

线程池是一种妥协,而不是适合不妥协,高性能的应用程序。 总线程控制。

不要担心OS调度器。 如果您的应用程序是CPU密集型的,长的计算是不大多是当地的L1&L2存储器访问它的性能更好的赌注每个线程绑定到自己的核心。 当然,操作系统将进来,但比起工作被你的线程执行的OS的工作是可以忽略不计。

此外,我应该说,我的线程的经验主要是从Windows NT引擎的机器。

__ __ _ _ _ _ 编辑 __ _ __ _

并非所有的内存访问都与读取和写入数据(见上文评论)。 一个经常被忽略的内存访问是取代码的执行。 所以我对住的核自己的高速缓存中的语句意味着确保所有必要的数据和代码驻留在这些缓存。 也请记住,即使是很简单的面向对象的代码可以生成隐藏调用库函数。 在这方面(代码生成部),面向对象和解释的代码比也许C(通常WYSIWYG)WYSIWYG少了很多或,当然,组件(完全WYSIWYG)。



Answer 2:

你总是会得到这样的曲线,这就是所谓的Amdahl定律 。
现在的问题是它将如何尽快稳定下来。

你说你检查你的代码的瓶颈,让我们假设是正确的。 然后还有就是显存带宽等硬件因素。



Answer 3:

在多个线程回报普遍下降可能预示着某种瓶颈。

是否有任何共享资源,喜欢收藏或队列或某事或者您使用的可能是依赖于一些有限的资源一些外部的功能呢?

在8个线程的犀利的突破有趣的是,在我的评论,我问如果CPU是一个真正的16核或8核,超线程技术,每个内核显示为2芯到OS。

如果是超线程,你要么有超线程不能在核心的性能,或存储器流水线的核心,通过放不能处理两倍的数据翻倍这么多的工作。

由线程甚至完成的工作或者是一些线程做得比别人多,这也可能表明资源匮乏。

由于您的加入的线程查询非常频繁的数据,表示在等待一个非常大的风险。

有没有什么办法让线程获取更多的数据每一次? 喜欢读书的10个项目,而不是一个?



Answer 4:

如果你正在做内存密集型的东西,你可能会击中缓存容量。

也许你可以用模拟算法只是处理相同的小一点,如果一遍又一遍数据,所以这一切应该适合在高速缓存中进行测试。

如果它确实是高速缓存,可能的解决方案可以使线程对同一数据的工作以某种方式(如小数据窗口的不同部分),或只需要调整算法更局部(如在整理,归并排序通常比快速排序慢,但它更多的缓存友好仍然使得它更在某些情况下)。



Answer 5:

是你的线程读取和存储并拢写入项目? 那么你很可能运行到错误共享。 如果线程1只适用于数据[1],[2],那么即使在一个理想的世界里,我们知道,连续两次由线程2数据[2]的读取总是会产生相同的结果,在现实世界中的线程2与数据打交道,如果线程1的更新数据[1]的某个时候这两个之间读取,则CPU将标记高速缓存为脏并进行更新。 http://msdn.microsoft.com/en-us/magazine/cc872851.aspx 。 为了解决这个问题,确保每个线程正在与数据是足够远在存储从其他线程正在使用的数据。

这可能给你的性能提升,但可能不会让你到16倍,也有很多事情在引擎盖下怎么回事,你只需要敲出来一个接一个。 说实在的,没有那么多线程当你的算法以30%的速度运行; 它更是你的单线程算法是运行在%的速度300,通过各种CPU的启用和缓存迷死是运行多线程有困难时趁势。 因此,有什么可“为难”一下。 但也有一些勤奋,你或许可以得到多线程版本近300%的速度工作,以及。

此外,如果您要统计超线程内核为真正的核心,那么,他们不是。 他们只允许当一个被阻塞的线程来的真快掉。 但是,他们永远不会让你以两倍速度运行,除非你的线程被阻塞反正半个月的时间,在这种情况下,已经意味着你有机会加速。



文章来源: Parallel code bad scalability