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