各位程序员好。我已经问了一个问题,但尽管我得到了非常好的答案,但我无法解决我的问题。然后,我花时间重构我的代码,以提高它的并行化潜力(通过更少的计算批次和更多的计算任务)。但是我仍然不能有比串行处理更好的性能。
我怀疑这种缓慢的并行处理是由于上下文切换造成的。或者可能是由于公共对象的“自动”同步。我想你可以帮助我了解发生了什么。
让我陈述一下我的情况:我正在编写一个用于科学计算的程序。它不依赖于外部事物,只依赖于我在开始时给它的输入值。这个问题的大小可以通过Ns
(这是我使用的名称)来衡量。它可以看作是解的“分辨率”,是用户输入的一种,通常在 100 左右。
这样,我的主类中有几个双精度数组,例如 double ys[Ns][N]
or phiS[Ns][Nord][N]
,其中 N 和 Nord 是程序的其他固定量值。在我的程序中,我必须为每个Ns
点计算几件事,这就是并行化。每个点计算都是独立的,所以我可以将它们分成不同的线程并希望它变得更快。
因此,我没有使用循环,而是for (int i=0; i<Ns; <i++)
将此计算任务划分为 Runnable 批次,每个批次都在一个较小的区间内:for (int i=start; i<end; i++)
,其中 start 和 end 始终介于 0 和 Ns 之间。例如,如果我在双核 PC 上,我会制作两批,一批使用start = 0
and end = Ns/2
,另一批使用start = Ns/2
and end = Ns
。如果我在四核上,第二批将不得不start = Ns/4
等等end = Ns/2
(假设在每种情况下划分都是准确的)。
每个 Batch 作为实现 Runnable 的类,都存储在 a 中ArrayList<Batch>
,并赋予 aFixedThreadPool
大小等于内核数。CountDown
它使用简单的方案执行批处理并等待它们完成。
这些批次中的每一个都需要从程序的主类访问这些数组上的数据,但是它们的访问是这样的,每个批次只能从yS[start][]
to读取yS[end][]
,因此两个批次永远不会尝试读取相同的数组元素。我想知道 Java 是否仍然锁定 yS,即使每个批次都没有尝试访问与其他批次相同的元素。
我还想知道我的问题是否与上下文切换导致的开销有关,因为每个批次都需要处理数千个双打,以及程序的构建方式是否会影响它。
也许我应该找到一种方法将与其相关的数组元素传递给每个批次,但我不知道如何解决这个问题。如果有指针,我可以通过简单的指针操作获得仅包含所需元素的新数组,而无需重新分配任何内容。有没有办法在 Java 中做这样的事情?
好吧,最后,提一下:有一部分代码需要同步(它处理其他数组)并且它已经可以正常工作了。我上面描述的计算任务并不是我的程序唯一要做的事情。它们在一个循环中,与顺序处理部分交替,但作为总执行时间确实很重要。
所以,总而言之,问题是:为什么我没有从多线程中获益,当我期望的时候?
我刚刚在这里运行了几次普通串行和多线程程序,串行和多线程分别为 14500 毫秒和 15651 毫秒。两者都在同一个双核上。其他需要注意的点:在串行运行中,每个计算任务(从 0 到 Ns)大约需要 1.1 到 4.5 ms。从双线程开始,每批(Ns/2 个点)大约需要 0.5 到 3 毫秒;(从上到下测量 run() 方法。每次计算任务因它自己的数值收敛而异)
非常感谢您的关注。