处理器核心数与线程池大小的比较

时间:2013-01-28 05:36:34

标签: java multithreading

我多次听说最好将线程池中的线程数保持在该系统中的核心数之下。拥有两次或更多线程而不是内核数量不仅是一种浪费,而且还可能导致性能下降。

那些是真的吗?如果没有,那些揭穿这些主张的基本原则是什么(特别是与java有关)?

4 个答案:

答案 0 :(得分:23)

  

我多次听说最好将线程池中的线程数保持在该系统中的核心数之下。拥有两次或更多线程而不是内核数量不仅是一种浪费,而且还可能导致性能下降。

声明不是作为一般声明。也就是说,有时候它们是真的(或者是真实的),有时它们显然是错误的。

有些事情无可争辩地是真的:

  1. 更多线程意味着更多的内存使用量。每个线程都需要一个线程堆栈。对于最近的HotSpot JVM,最小线程堆栈大小为64Kb,默认值可以高达1Mb。这可能很重要。此外,任何活动的线程都可能拥有或共享堆中的对象,无论它是否当前是可运行的。因此,期望更多线程意味着更大的内存工作集是合理的。

  2. 与执行硬件上的内核(或超线程内核或其他内容)相比,JVM实际上不能运行更多线程。如果没有引擎,汽车将无法运行,如果没有核心,线程将无法运行。

  3. 除此之外,事情变得不那么明确了。 “问题”是活动线程可以处于各种“状态”。例如:

    • 活动线程可以正在运行;即积极执行指示。
    • 活动线程可以运行;即等待核心以便它可以运行。
    • 活动线程可以通过同步;即等待来自另一个线程的信号,或等待锁被释放。
    • 活动线程可以等待外部事件;例如等待一些外部服务器/服务响应请求。

    “每个核心一个线程”启发式假定线程正在运行或可运行(根据上述内容)。但是对于很多多线程应用程序来说,启发式是错误的......因为它没有考虑其他状态中的线程。

    现在“太多”线程显然可以导致显着的性能下降,使用太多内存很简单。 (想象一下,你拥有4Gb的物理内存,并且你可以使用1Mb堆栈创建8,000个线程。这是虚拟内存抖动的一个秘诀。)

    但其他事情呢?可能有太多线程导致过多的上下文切换?

    我不这么认为。如果您有很多线程,并且您的应用程序使用这些线程可能导致过多的上下文切换,并且 对性能不利。但是,我认为上下文切换的根本原因不是实际的线程数。性能问题的根源更可能是应用程序:

    • 以特别浪费的方式同步;例如在Object.notifyAll()更好的时候使用Object.notify(),或者
    • 在高度竞争的数据结构上进行同步,或者
    • 相对于每个线程正在执行的有用工作量进行太多同步,或者
    • 尝试并行执行过多的I / O.

    (在最后一种情况下,瓶颈很可能是I / O系统而不是上下文切换......除非I / O是同一台机器上的服务/程序的IPC。)

    另一点是,在没有上述混淆因素的情况下,拥有更多线程不会增加上下文切换。如果你的应用程序有N个可运行的线程竞争M个处理器,并且线程纯粹是计算和无争用的,那么OS的线程调度程序将尝试在它们之间进行时间分片。但是,时间片的长度很可能以十分之一秒(或更长)来测量,因此与CPU绑定线程在其切片期间实际执行的工作相比,上下文切换开销可以忽略不计。如果我们假设时间片的长度是常数,那么上下文切换开销也将是恒定的。添加更多可运行的线程(增加N)不会显着改变工作与开销的比率。


    总之,“太多线程”确实对性能有害。然而,对于有多少“太多”,没有可靠的通用“经验法则”。而且(幸运的是)在“太多”的性能问题变得显着之前,你通常会有相当大的余地。

答案 1 :(得分:5)

线程数少于核心数通常意味着无法利用所有可用内核。

通常的问题是你想要的核心数多于核心数。但是,这会有所不同,具体取决于线程花费在I / O上的时间(总体)与执行计算所花费的时间。如果他们都在进行纯计算,那么您通常需要与核心数相同的线程数。如果他们做了相当多的I / O,你通常需要比核心更多的线程。

从另一个方向看一下,你需要运行足够多的线程来确保每当一个线程由于某种原因(通常在I / O上等待)阻塞时你有另一个可以运行的线程(没有阻塞)那个核心。确切的数字取决于每个线程花费多少时间被阻止。

答案 2 :(得分:4)

这不是真的,除非线程数远远超过核心数。原因是额外的线程意味着额外的上下文切换。但事实并非如此,因为如果这些上下文切换是有益的,操作系统将只进行非强制上下文切换,而其他线程不会强制进行额外的上下文切换。

如果创建了一个荒谬的线程数,那就浪费了资源。但是,与创建太少线程的糟糕程度相比,这一切都没有。如果创建的线程太少,则意外的阻塞(例如页面错误)可能导致CPU处于空闲状态,并且会从一些额外的上下文切换中淹没任何可能的伤害。

答案 3 :(得分:1)

不完全正确,这取决于整体软件架构。如果某些线程被OS暂停,因为它们正在等待I / O完成,那么保留比可用内核更多的线程是有原因的。这可能是显式的I / O调用(例如从文件同步读取),也可能是隐式的,例如系统分页处理。

实际上我在一本书中读到,将线程数保持两倍于CPU内核的数量是一个很好的做法。