【问题标题】:How much does parallelization help the performance if the program is memory-bound?如果程序受内存限制,并行化对性能有多大帮助?
【发布时间】:2012-08-13 11:34:41
【问题描述】:

我并行化了一个 Java 程序。在具有 4 核的 Mac 上,以下是不同线程数的时间。

threads #   1         2          4           8          16
time 2597192200 1915988600  2086557400  2043377000  1931178200

在具有两个套接字的 Linux 服务器上,每个套接字有 4 个内核,下面是测量的时间。

threads #   1         2          4           8          16 
time 4204436859 2760602109  1850708620  2370905549  2422668438

如您所见,加速比远离线性加速。在这种情况下几乎没有并行化开销,例如同步或 I/O 依赖项。

我有两个问题:

  1. 这些数据是否暗示此 Java 程序受内存限制?
  2. 如果是这样,有没有什么方法可以在不改变硬件的情况下进一步提高性能?

【问题讨论】:

  • 线程绑定(即不允许操作系统在 CPU 之间移动线程)对于内存绑定程序非常重要,尤其是在 NUMA 系统上运行时。请参阅this answer,了解有关内存带宽如何随不同系统上的线程数扩展的线索。

标签: multithreading performance optimization parallel-processing memory-bandwidth


【解决方案1】:

回答标题问题

Amdahl's Law 解释说,程序并行化所获得的加速取决于程序中有多少是可并行化的。

我们还必须增加协调并行性的开销。

因此,我们会考虑程序的哪些百分比/部分是可并行化的,以及会产生哪些开销(同步、通信、错误共享等)。

读取内存是否可并行化?

从硬盘驱动器

您可以同时从 2 个不同的硬盘驱动器读取数据而不会降低速度。

但是,通常并行性并不能加快从硬盘读取的速度。

硬盘驱动器(即带有旋转磁盘的驱动器)已针对顺序读取进行了优化,在内存位置之间跳转会减慢整体内存传输速度。

固态硬盘实际上非常擅长随机访问数据,在内存中到处乱跳,所以固态硬盘保持读/写队列满是个好主意。

从 RAM 和缓存

了解缓存行的概念将有助于避免错误共享。

这种类型的内存操作可以有效地并行化,例如通过将数组划分为四个分区来迭代数组。

您的问题

我假设您的时间以纳秒为单位,因此在计算机 1 上,程序耗时 2.5 秒,然后稳定到大约 2 秒,峰值为 1.9 秒。

我希望您同时运行的后台程序最少,并且您执行了几次这些测试以消除异常情况。

另外,由于 Java 虚拟机的即时编译 (JIT),可能会出现计时不规则,因此要准确计时,您需要循环运行代码几次,并将时间存储最后一次迭代。 (或预编译为本机代码)。

此外,由于程序第一次运行,硬盘驱动器中使用的大部分数据将被移动到缓存中,因此以后的执行应该更快。 (所以要么使用循环后最后一次运行的时间来确保内存在缓存中,或者使用第一个时间但在时间之间关闭并打开计算机)。

程序内存是否已绑定?

仅根据您的时间安排,这很难说。

第一台计算机用了 2.5 秒,然后用 2 个线程加速了 20%,但随后保持在 2.0 秒左右。

就其本身而言,这种加速可能只是 JIT 和缓存内存被 1 个线程上的时间填充的结果。之后,运行时的任何差异都可能只是噪音。

第二台计算机用了 4.2 秒,然后是 2.8,然后是 1.9,然后又回到了大约 2.3 秒。

这似乎确实展示了某种类型的并行加速,但发生了一些争用时间(内存、缓存行、同步等),时间从 4 个线程增加到 8 个线程就证明了这一点线程。

有什么方法可以提高性能?

对代码使用分析器,确定代码的哪些部分占用的时间最多。

(您可以通过调试代码并中断并查看程序在哪里来模拟分析器。重复 10 次,看看是否有一个部分比另一部分更按比例停止。)

针对问题使用更好的算法或以更好的方式排列内存中的数据(数据结构)。

在问题中利用更多的并行性。

尝试使硬盘内存读取顺序。也许只有一个线程从硬盘读取数据,然后将数据放入并发队列中,由其他线程操作。

【讨论】:

  • 内存操作只有在有足够的带宽来满足数据传输的需求时才能并行化。即使仅由一个线程执行,单个向量化循环(尤其是使用 AVX 机器)也可以轻松地使单个内存链接饱和。在这种情况下,在两个线程之间拆分循环不会提高性能,即使没有错误共享或添加显式同步。
【解决方案2】:

嗯,它们暗示该算法不受 CPU 限制。它可能受其他东西的约束 - 它可能是内存、I/O 或其他东西,但它可能不受 CPU 限制。

【讨论】:

  • 如何知道并验证任务是否受内存限制?例如,我正在尝试将 2 个大矩阵相乘,但多线程并没有看到任何收益。我如何确定这是由于内存瓶颈造成的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-11
  • 2015-03-07
相关资源
最近更新 更多