设置线程池的理想大小

38

在操作系统和编程角度上,newSingleThreadExecutornewFixedThreadPool(20)有什么区别?

每当我使用newSingleThreadExecutor运行程序时,程序表现良好,端到端延迟(95th百分位)约为5ms。但是,一旦我开始使用newFixedThreadPool(20)运行程序,我的程序性能就下降了,我开始看到端到端延迟为37ms

因此,现在我正在尝试从架构角度理解这里的线程数意味着什么?如何决定选择最佳线程数?如果我使用更多的线程会发生什么?

如果有人能用通俗易懂的语言向我解释这些简单的事情,那将对我非常有用。感谢您的帮助。

我的机器配置规格-我正在从Linux机器运行我的程序:

processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 45
model name      : Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz
stepping        : 7
cpu MHz         : 2599.999
cache size      : 20480 KB
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 syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 popcnt aes hypervisor lahf_lm arat pln pts
bogomips        : 5199.99
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:

processor       : 1
vendor_id       : GenuineIntel
cpu family      : 6
model           : 45
model name      : Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz
stepping        : 7
cpu MHz         : 2599.999
cache size      : 20480 KB
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 syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 popcnt aes hypervisor lahf_lm arat pln pts
bogomips        : 5199.99
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:

1
你对这里的答案不满意吗:https://dev59.com/BXHYa4cB1Zd3GeqPIjO7? - AllTooSir
我想更详细地了解。我发布的那个问题更多关于编程和寻找瓶颈,但Gray建议可能是线程大小的问题。所以我想发另一个问题,但这次更具体地从架构角度来看。 - arsenal
3个回答

56

好的。理想情况下,假设您的线程没有锁定以使它们彼此不阻塞(相互独立),并且您可以假设工作负载(处理)相同,则结果是,使用Runtime.getRuntime().availableProcessors()availableProcessors() + 1的池大小会产生最佳结果。

但是,如果线程彼此干扰或涉及I/O,则Amadhal's定律可以很好地解释。从维基百科中,

 

Amdahl's定律指出,如果P是程序可以并行执行(即受益于并行化)的比例,并且(1-P)是无法并行化的部分(仍然串行),则使用N个处理器可以实现的最大加速比为

Amadhal law

在您的情况下,根据可用核心数量以及它们确切执行的工作(纯计算? I / O? 持有锁定? 由于某些资源被阻止而处于阻塞状态? 等等...),您需要根据上述参数提出解决方案。

例如:几个月前,我参与了从多个网站收集数据的工作。我的计算机有4个核心,我有一个池大小为4的线程池。但是由于操作纯粹是I / O,而且我的网络速度还不错,我意识到使用7个线程池大小可以获得最佳性能。这是因为线程不是争夺计算能力,而是争夺I/O。因此,我可以利用更多的线程积极地竞争核心的事实。

附:我建议阅读Brian Goetz的书《Java并发编程实践》中的性能章节。它详细讨论了此类问题。


感谢Jatin的建议。关于核心大小,我也在Linux中发布了我的机器的配置规格,你能找出我拥有的核心大小吗?因为我无法通过查看配置规格来找到它。 - arsenal
1
@TechGeeky 调用 Runtime.getRuntime().availableProcessors() 将会返回你的处理器核心数。 - Jatin
是的,看起来我的负载和性能机器只有2个内核。而我正在使用20个线程运行我的程序,这就是为什么我在程序中看到如此高的性能问题的原因。对吗? - arsenal
@TechGeeky 当然可以。20实际上是一个杀手:P。我建议根据Amadhal定律设置大小,并通过增加或减少池大小来进行调整。 - Jatin
完全同意@Jatin的观点,可用处理器的线程池大小将会得到最佳结果。 - Sumanth Varada
显示剩余5条评论

7
现在我正试图从架构角度理解这里的线程数量意味着什么?每个线程都有自己的堆栈内存、程序计数器(类似于指向下一个执行指令的指针)和其他本地资源。交换它们会损害单个任务的延迟。好处是,当一个线程空闲(通常在等待I/O时)时,另一个线程可以完成工作。此外,如果有多个可用的处理器,在任务之间没有资源和/或锁争用的情况下,它们可以并行运行。
如何确定我应该选择的最佳线程数?交换价格与避免空闲时间的机会之间的权衡取决于任务的细节(有多少I/O,何时进行I/O,I/O之间有多少工作量,使用多少内存来完成)。实验始终是关键。
如果我使用更多的线程会发生什么?通常会首先出现线性增长,然后是相对平坦的部分,然后是下降(可能非常陡峭)。每个系统都是不同的。

5

看阿姆达尔定律是可以的,特别是如果您确切地知道P和N有多大。由于这永远不会真正发生,您可以监视性能(无论如何都应该这样做),并增加/减少线程池大小以优化对您重要的任何性能指标。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接