多线程Java不会加速

时间:2011-11-15 14:53:31

标签: java multithreading

我在Java中实现了一个简单的并行合并排序算法。这会将数组切割成相等的部分,并将它们传递给每个线程独立排序。对数组段进行排序后,它们将由单个线程合并。由于没有共享资源,因此在对子列表进行排序时不会使用同步。合并结果数组的最后一个线程虽然等待其他线程完成。

当使用两个线程时,性能增益几乎达到66%。当我使用4个线程时,所用时间与2个线程版本没有区别。我在linux 2.6.40.6-0.fc15.i686.PAE和Intel Core i5上。

我使用unix time命令对时间进行基准测试(数组被赋予统一的随机整数)。在排序结束时,我正在检查数组排序是否正确(不是并行)。

1个线程
$ echo "100000000" | time -p java mergeSortTest

Enter n: 
[SUCCESS]

real 40.73
user 40.86
sys 0.22

2个主题
$ echo "100000000" | time -p java mergeSortTest

Enter n: 
[SUCCESS]

real 26.90
user 49.65
sys 0.48

4个线程
$ echo "100000000" | time -p java mergeSortTest

Enter n: 
[SUCCESS]

real 25.13
user 76.53
sys 0.43

使用4个线程时CPU使用率约为80%至90%,使用2个线程时约为50%,使用单线程时约为25%。

当我在4个线程中运行时,我期待一些加速。我在任何地方都错了。

更新1

以下是代码:http://pastebin.com/9hQPhCa8

更新2 我有一台Intel Core i5第二代处理器。

cat /proc/cpuinfo | less的输出(仅显示核心0)。

processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 42
model name      : Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz
stepping        : 7
cpu MHz         : 800.000
cache size      : 3072 KB
physical id     : 0
siblings        : 4
core id         : 0
cpu cores       : 2
apicid          : 0
initial apicid  : 0
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx rdtscp lm constant_tsc arch_perfmon pebs bts xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 x2apic popcnt xsave avx lahf_lm ida arat epb xsaveopt pln pts dts tpr_shadow vnmi flexpriority ept vpid
bogomips        : 4589.60
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:

8 个答案:

答案 0 :(得分:10)

intel core i5-xxM系列有2个内核,因此使用2个以上的线程会因为更多的上下文切换而降低性能。

修改

以下是我的答案的扩展,其中我讨论了Core i7 architecture - 可能影响CPU性能和特定内存密集型操作(如排序)的特定因素。

涡轮增压技术

Intel Core i7具有可变的处理器频率。在高负载时,频率将受到热量的限制,从而降低了利用更多内核的性能增益。

共享L3缓存

对大型数据集进行排序(>>> 8 Mb)将导致大量L3页面错误。使用太多线程可能会增加页面错误的数量,从而降低效率。我不确定mergesort是否属于这种情况。 (顺便说一句:你如何测量Linux中的L3缓存未命中?) 不过,我不确定这是一个因素。

我必须说,我很惊讶你使用i7的所有四个内核都没有得到任何性能提升。我本周末会尝试在家里进行一些测试。

答案 1 :(得分:9)

Core i5拥有2个内核和超线程技术,因此它似乎有4个内核。这些额外的两个逻辑内核几乎没有两个物理内核的帮助,因为您的排序算法在保持CPU忙碌方面做得很好。

由于您要求提供“可信”的来源,我将指出英特尔网站上的一篇文章,我之前读过这篇文章:performance-insights-to-intel-hyper-threading-technology。特别要注意以下关于“超线程限制”的部分:

  

极其计算效率的应用程序。如果处理器执行   资源已被充分利用,然后几乎没有   通过启用Intel HT技术获得。例如,代码   已经可以执行每个周期四个指令不会增加   在启用Intel HT技术的情况下运行时的性能   进程核心每个最多只能执行四条指令   周期。

另请注意有关内存子系统争用的此部分:

  

极高的内存带宽应用程序。英特尔HT技术增加了运行两个内存子系统时对内存子系统的需求   线程。如果应用程序能够利用所有内存   带有Intel HT技术禁用的带宽,那么性能   启用Intel HT技术时不会增加。有可能的   在某些情况下,由于增加,性能会降低   这些实例中的内存需求和/或数据缓存效果。该   好消息是基于Nehalem核心的系统集成了   内存控制器和英特尔®QuickPath互连非常重要   与旧的Intel CPU相比,可用内存带宽增加   采用Intel HT技术。结果是数量   使用Intel HT会降级的应用程序   由于内存带宽不足,Nehalem核心的技术是   大大减少了。

其他有趣的观点可以在Intel Guide for Developing Multithreaded Applications中找到。以下是detecting-memory-bandwidth-saturation-in-threaded-applications的另一个片段:

  

随着越来越多的线程或进程共享限制   缓存容量和内存带宽的资源,可扩展性   线程应用程序可能会受到限制。内存密集型   线程应用程序可能会遭受内存带宽饱和   引入更多线程。在这种情况下,线程应用程序   不会按预期扩展,性能可以降低。

答案 2 :(得分:4)

另一个可能的问题可能是虚假分享。

http://en.wikipedia.org/wiki/False_sharing

http://drdobbs.com/go-parallel/article/showArticle.jhtml?articleID=217500206

这取决于合并排序访问边界缓存行中元素的频率。对于小于千字节左右的数据集,这可能会更加明显。

答案 3 :(得分:3)

我在i7上尝试了这个,即使有4个核心,也没有从2-4个线程中获得改进。我怀疑问题是您的数据不适合缓存,因此您正在测试单内存总线的吞吐量。

答案 4 :(得分:3)

我在带有-server JVM选项,100000000 int和2GB Xmx内存的双核i7上获得预期结果:

1个帖子:23秒
2个主题:14秒
4个主题:10秒

我还删除了手动垃圾收集,并在同一个JVM实例中按顺序执行排序,首先进行预热排序。

正如史密斯先生所评论的那样,微基准测试(JVM热点)有点复杂,我可能会为4个以上的内核添加它,合并排序可以执行现在与单个线程相对的线程数量的一半,所以你的基准并不完全适用于多线程方法。

您可能还想查看this问题。

答案 5 :(得分:2)

作为比较,尝试使用java 7和fork join框架进行合并排序。有一个如何做到here的例子。这将显示您的实施是否存在问题,或是机器的限制。

答案 6 :(得分:1)

有趣,因为我在尝试并行化合并排序时有相同的观察结果。尝试了两种不同的工作产卵方法,但没有加快速度。并行化合并排序的方法是使合并并行。并在不同的核心上进行单独合并?在这种情况下,递归被切断,线程数会影响加速。再次加速不能超过串行速度。这种技术出现在Morgan Kaufman的结构化并行编程模式和实践书中。

A parallel merge sort

答案 7 :(得分:0)

我最近不得不在i7架构上比较冒泡排序,合并排序和bitonic排序。我使用这里给出的第一个代码进行合并并遇到了同样的问题:8threads并不比4好。然后我读了SMT(英特尔超线程)的东西,发现了问题和解决方案:

在合并方法中删除以下行:

  

if(Runtime.getRuntime()。freeMemory()<((n1 + n2)* 4))             Runtime.getRuntime()。 gc();

这段代码释放了物理内核的内存con L1和L2级别,但是在那些kbs中我们有两个逻辑线程(不仅是一个)的缓冲区,因此一个线程擦除了该内核中兄弟线程的缓冲区

删除 if 之后,我看到了SMT提供的4到8个线程之间的1.25改进。 如果有人可以在i5上尝试这个很棒的话。