并行化:导致Java线程阻塞而不是同步的原因。 I / O?

时间:2008-12-02 07:49:56

标签: java multithreading parallel-processing

简短版本在标题中。

长版: 我正在研究一个使用Java进行科学优化的程序。程序的工作量可以分为并行和串行阶段 - 并行阶段意味着正在执行高度可并行化的工作。为了加速程序(它运行数小时/天),我创建了许多线程,这些线程等于我正在使用的机器上的CPU核心数 - 通常是4或8 - 并在它们之间划分工作。然后我开始这些线程并加入()它们,然后再进入连续阶段。

到目前为止一切顺利。令我困扰的是,并行阶段的CPU利用率和加速率远不及“理论最大值” - 例如如果我有4个内核,我希望看到介于350-400%之间的“利用率”(如上图所示),而是在180到310之间反弹。仅使用一个线程,我获得100%的CPU利用率。 / p>

我知道线程不能全速运行的唯一原因是:     由I / O引起的阻塞     因同步而产生的阻塞

我的并行线程中没有任何I / O,也没有任何同步 - 线程共享的唯一数据结构是只读的,并且是基本类型或(非并发)集合。所以我正在寻找其他解释。一种可能性是多个线程反复阻塞垃圾收集,但这在内存压力的情况下似乎才有意义,而且我的分配远远高于所需的最大堆空间。

任何建议都将不胜感激。

更新:万一有人好奇,经过一些调查后,我调整了代码以获得一般性能并且看到更好的利用率,即使我改变的任何内容都与同步有关。但是,一些更改应该导致更少的新堆分配,特别是我摆脱了一些使用迭代器临时盒装数字(CERN“Colt”库,高性能Java计算在这里很有用:它为基本类型提供了IntArrayList,DoubleArrayList等集合。所以我认为垃圾收集可能是罪魁祸首。

7 个答案:

答案 0 :(得分:5)

所有图形操作都在单个线程上运行。如果他们渲染到屏幕,他们将有效地争夺对该线程的访问权。

如果您在Windows上运行,则无论如何,所有图形操作都在单个线程上运行。其他操作系统也有类似的限制。

实际上很难获得适当的线程工作者粒度,有时很容易使它们太大或太小,这通常会使所有核心的使用率低于100%。

如果你没有渲染太多gui,那么最可能的罪魁祸首就是你对某些共享资源的争论比你想象的要多。使用jprofiler等分析器工具很容易看到这一点。有些VM就像bea的jrockit甚至可以直接告诉你这个。

这是你不想做出猜测的地方之一。获取个人资料!

答案 1 :(得分:4)

首先,GC不会仅在“内存压力不大的情况下”发生,但在任何时候JVM都认为合适(据我所知,这是不可预测的)。

其次,如果您的线程在堆中分配内存(您提到它们使用集合,所以我猜它们确实在堆中分配内存),您永远无法确定此内存当前是在RAM中还是在虚拟内存页面上(操作系统决定),因此访问“内存”可能会生成阻止I / O访问!

最后,正如在先前的回答中所建议的那样,您可能会发现使用分析器检查发生了什么是有用的(甚至JMX监控可能会给出一些提示)。

我相信除非您提供更具体的(代码)信息,否则很难获得有关您问题的进一步提示。

答案 2 :(得分:2)

首先,我假设你没有在盒子上做任何其他重要的工作。如果你是,那显然会弄乱事情。

如果你真的没有分享任何东西,那听起来很奇怪。您能否让我们更多地了解代码的真正含义?

如果您将程序的n个副本作为不同的Java进程运行,每个只使用一个线程,会发生什么?如果完全使用每个CPU,那么至少我们知道操作系统不会出现问题。说到操作系统,运行哪一个,哪个JVM?如果您可以尝试不同的JVM和不同的操作系统,结果可能会给您一个关于错误的提示。

答案 3 :(得分:1)

同样重要的一点:您使用哪种硬件? 例如。 4-8核心可能意味着你在Suns Niagara CPU上工作。尽管有4-8个核心,但它们的FPU更少。在计算科学的东西时,它可能发生,FPU是瓶颈。

答案 4 :(得分:0)

您尝试使用完整的CPU功能进行计算,但操作系统本身也使用资源。因此请注意,操作系统将阻止您的一些执行,以满足其需求。

答案 5 :(得分:0)

您正在某种程度上进行同步。

也许只在内存分配系统中,包括垃圾收集。虽然JVM供应商努力将这些领域的阻塞降至最低,但他们无法将其降低到零。也许你的应用程序的某些方面正在推动这个领域的一个弱点。

公认的智慧是“不要建立自己的内存回收池,让GC为你工作”。这在大多数情况下都是正确的,但至少在我维护的一段代码中没有(通过分析证明)。也许您需要以某种主要方式重新编写对象分配。

答案 6 :(得分:0)

尝试使用JRockit Mission Control附带的延迟分析器。如果应用程序正在等待文件I / O,TLA提取,对象分配,线程挂起,JVM锁定,gc-pauses等,它将向您显示CPU正在执行的操作。您还可以看到转换,例如当一个线程唤醒另一个线程时。开销可以忽略不计,1%左右。

有关详细信息,请参阅此blog。该工具可以免费用于开发,您可以下载它here