【问题标题】:Number of processor core vs the size of a thread pool处理器核心数与线程池大小
【发布时间】:2013-01-11 10:29:57
【问题描述】:

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

这些是真的吗?如果不是,那么揭穿这些说法的基本原则是什么(特别是与 java 相关)?

【问题讨论】:

标签: java multithreading


【解决方案1】:

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

作为一般性陈述,声明不正确。也就是说,有时它们是真实的(或真实的),有时它们显然是错误的。

有几件事是无可争辩的:

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

  2. JVM 实际运行的线程不能多于执行硬件上的内核(或超线程内核或其他)。汽车没有引擎就无法运行,线程没有核心就无法运行。

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

  • 可以运行活动线程;即主动执行指令。
  • 可以运行活动线程;即等待内核运行。
  • 活动线程可以通过同步;即等待来自另一个线程的信号,或等待释放锁。
  • 活动线程可以等待外部事件;例如等待某个外部服务器/服务响应请求。

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

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

但是其他的呢?线程过多会导致过多的上下文切换吗?

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

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

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

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


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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      通常的问题是您想要的线程数多于内核数。但是,这会有所不同,具体取决于您的线程花费在 I/O 之类的事情上的时间量(总体)与它们花费在计算上的时间量。如果它们都在进行纯计算,那么您通常需要与内核数量相同的线程。如果他们执行大量 I/O,您通常需要比内核多得多的线程。

      从另一个方向看片刻,您希望运行足够多的线程以确保每当一个线程因某种原因阻塞(通常等待 I/O)时,您就有另一个线程(未被阻塞)可用于运行那个核心。所需的确切数字取决于每个线程阻塞的时间。

      【讨论】:

      • “如果他们正在进行大量的 I/O”,假设 I/O 由单独的处理器处理(我记不起这个硬件的确切词)并且 CPU 是理想的。
      • @Nambari:当线程执行大部分 I/O 时,线程会暂停,直到 I/O 完成。然后它就可以运行了,必须重新安排。如果您没有另一个准备好运行的线程,那么在 I/O 完成时该内核将被浪费——等待磁盘或其他任何东西。
      【解决方案4】:

      这不是真的,除非线程数远远多于内核数。原因是额外的线程将意味着额外的上下文切换。但这不是真的,因为操作系统只会在上下文切换有益的情况下进行非强制的上下文切换,并且额外的线程不会强制额外的上下文切换。

      如果您创建的线程数量过多,则会浪费资源。但与创建太少线程的糟糕程度相比,这一切都不算什么。如果您创建的线程太少,意外的块(例如页面错误)可能会导致 CPU 处于空闲状态,而这会消除一些额外的上下文切换可能带来的任何危害。

      【讨论】:

        猜你喜欢
        • 2016-03-20
        • 2021-10-03
        • 2022-01-23
        • 2022-06-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多