多核机器上性能结果
本文实例包括了下面的Java类:
1. CoreSort 测试核心实现Arrays.sort()。
2. ConcurrentSort是对相同方法的一个简单的并行化实现,这个方法利用了多核CPU的优点。
3. TestConcurrentSort测试了在不同的输入情况下并行化排序实现结果。
1. CoreSort 测试核心实现Arrays.sort()。
2. ConcurrentSort是对相同方法的一个简单的并行化实现,这个方法利用了多核CPU的优点。
3. TestConcurrentSort测试了在不同的输入情况下并行化排序实现结果。
下面的结构是在多核Intel处理器上得到的,每个核的运行频率在1.66Ghz。使用并行化版本,核心的Arrays.sort方法来对数组进行排序,这个数组随机产生了百万个字符串。没有做并行化处理平均运行时间是3350ms,进行并行化处理的运行时间是2180ms(提高了35%).
在理想的情况下,性能的提高幅度可以达到50%。但是,由于一些原因,你几乎能达到这个效果。首先,创建和运行多线程就会引起一些“消耗”。第二,大多数问题涉及到一些“归并”阶段,延误一些时间。第三,子任务之间还需要通信。
这容易吗?
那么,这样做容易吗?没有定论。如果是固有的并发操作,那么问题就很简单了,你只需要使用JDK 6.0 并发API,写几行代码就可以完成。但是,如果你的代码是给规模不固定,任意数量的处理器的话,那么你的代码就不是很容易了,这样一来你的代码有可以用到摩尔定律了(只要硬件开发商保持在自己的芯片上安装更多的核)。
另外一方面,你现在并行程序设计的时候面临多方面的选择。首先,你需要决定如何将你的任务放在所有的处理器上,还是其中的一部分,或者只是一个固定的数量上。将任务分布在所有的处理上会获得一个不错的运行时间,但是实施起来会很复杂,并且在某些情况下(当一个核负载很大的时候),一个具体的子任务会比其它的任务花更长的时间。如果将一个任务分散在一个固定数量的核上,性能提高度就只是一个常数了,无论你有多少个可用的核。
第二,你的测试会变得越来越复杂。你需要测试你的代码在不同的配置条件下,包括单核,双核,四核等等。一些Bug变得很难复现出来。
第三,代码本身变得更加复杂,并且不仅仅是因为任务的细分导致的。你还需要考虑系统资源;你的方法或许要被多个线程调用。并且如果你使用了较多的线程,很容易导致系统死机。
最后,但是当然不是最不重要的,有一些问题并不是很轻易的就可以实现并行化的。举个例子,ArrayList.indexOf。这个方法非常简单,扫描整个数组,返回符合传入参数的第一个元素。这个任务很容易的拆分到任何的可用处理器上,每一个去查找自己的范围。但是这个方法和Arrays.sort的主要区别就是合并阶段。在indexOf方法,你需要返回第一个匹配元素的索引。但是如果子任务操作的第一个区域找到了匹配的元素,那么在ArrayList.indexOf的例子中,所有其它的子任务使用的处理器就没有什么意义了。
ArrayList.indexOf方法,不像Arrays.sort,它们不需要所有子任务的全部结果。如果不止一个子任务返回非负的结果,那么就应该最小的那个。想想你把一个查找任务拆分成四个子任务,并且第三那个子任务返回了匹配的索引。你不能停止剩余的子任务和返回的结果,只能不需等等,直到前面两个子任务完成,就是为了决定结果是什么。在这个具体的例子中,当一个子任务N返回了一个匹配的结果,你能停止所有的任务N以后的任务,但是你要等待N以前的任务的执行。
结论
多核的硬件用来进行客户端的开发已经成为现实了。对于那些在现有硬件伸缩行不是很好的硬件将不会采用。但是,并行化处理可以让你的代码享受多核硬件所有带来的优点。但是,这就意味你要将你旧的思维习惯从顺序模式转变到并行模式。并行模式不仅是提高性能,它也能带来自身的最优方法。如果你想让你应用程序性能拥有更强的竞争力,具有更大的伸缩性的话,你应该加入到并行化的阵营来。