多线程 Java 不会加速

2022-09-01 12:07:14

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

使用两个线程时,性能提升几乎达到 66%。当我使用4个线程时,所花费的时间与2个线程版本没有区别。我使用的是Linux和Intel Core i5。2.6.40.6-0.fc15.i686.PAE

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

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

Enter n: 
[SUCCESS]

real 40.73
user 40.86
sys 0.22

$ echo "100000000" | time -p java mergeSortTest

Enter n: 
[SUCCESS]

real 26.90
user 49.65
sys 0.48

$ echo "100000000" | time -p java mergeSortTest

Enter n: 
[SUCCESS]

real 25.13
user 76.53
sys 0.43

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

我期望在4线程中运行时会有一些加速。我在任何地方都错了吗?

更新 1

代码如下:http://pastebin.com/9hQPhCa8

更新 2我有一个英特尔酷睿 i5 第二代处理器。

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

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:

答案 1

英特尔酷睿 i5-xxM 系列有 2 个内核,因此使用 2 个以上的线程会由于更多的上下文切换而降低性能。

编辑:

以下是对我的答案的扩展,其中我讨论了Core i7架构特定的因素,这些因素可能会影响CPU的性能和内存密集型操作,例如排序。

涡轮增压技术

英特尔酷睿 i7 具有可变的处理器频率。在高负载下,频率将受到热量的限制,从而降低使用更多内核的性能增益。

共享 L3 高速缓存

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

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


答案 2

Core i5具有2个核心和超线程技术,因此似乎具有4个核心。这两个额外的逻辑内核几乎不会像两个物理内核那样有帮助,因为您的排序算法在保持CPU繁忙方面做得很好。

既然您要求一个“可信”的来源,我将指出我不久前在英特尔网站上读到的一篇文章:性能洞察到英特尔超线程技术。特别注意以下关于“超线程的局限性”的部分:

计算效率极高的应用程序。如果处理器的执行资源已经得到充分利用,那么启用英特尔超线程技术几乎没有什么好处。例如,在启用英特尔超线程技术的情况下运行时,已经可以每周期执行四条指令的代码不会提高性能,因为进程内核每个周期最多只能执行四条指令。

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

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

其他有趣的要点可以在英特尔多线程应用开发指南中找到。下面是检测内存带宽饱和线程应用程序中的另一个代码片段:

随着越来越多的线程或进程共享有限的缓存容量和内存带宽资源,线程应用程序的可伸缩性可能会受到限制。内存密集型线程应用程序可能会因引入更多线程而遭受内存带宽饱和的影响。在这种情况下,线程应用程序将无法按预期进行缩放,并且性能可能会降低。