背景资料
我有一个分布式处理应用程序,可以进行数据分析。它旨在并行处理实时更新的多组数据。作为设计的一部分,分析已经分解为分析节点。每个节点获取源数据并对其进行处理以创建其他数据,然后可以由其他节点使用。要对一个数据集进行当前的全套分析,需要大约200个节点。
在当前设计中,每个节点都使用自己的线程运行。现在,大多数时候这些线程都处于睡眠状态。每当数据更新时,他们就像瀑布一样唤醒,然后他们又回到睡眠状态。该应用程序目前正在生产中运行40组数据,每组需要200个节点,使用8000个线程。当没有数据进入时,服务器上没有负载。当数据以最繁忙的时间进入时,服务器的CPU占用率约为25%。这都在项目的设计和生产参数范围内。
现在进行下一步,我们将40组数据扩展为200.每组需要200个节点,这意味着总共40000个节点,即40000个线程。这超出了我们服务器的最大PID,所以我要求我们的服务器管理员增加上限。他们做到了,应用程序正常运行,但是他们给了我一些关于线程数量的推迟。我并不否认线程的数量是不寻常的,但我们的设计阶段是预期和保证的。
我正计划对设计进行一些小的调整,以将线程与节点分开。这将允许我们配置一个线程来运行多个节点,并减少我们的线程数。对于不经常更新的数据集,让一个线程在每个节点中执行数据更新的性能影响非常小。对于每秒更新数百次的数据集,我们可以将每个节点配置为在其自己的线程上运行。事实上,我不怀疑这种设计会发生什么变化 - 这只是时间问题。与此同时,我希望尽可能多地了解使用此设计的后果。
问题
在一台机器上运行超过40,000个线程的成本是多少?通过让JVM / Linux操作系统管理这么多线程,我失去了多少性能?请记住,当没有工作时,它们都已正确配置为睡眠状态。所以,我只是谈论额外的开销和线程数量导致的问题。
请注意 - 我知道我可以减少线程数量,而且我知道进行此设计更改是个好主意。我会尽快做到,但必须与其他工作和设计考虑因素保持平衡。我问这个问题是为了收集信息,以便做出正确的决定。非常感谢您对这种性质的想法和评论。
答案 0 :(得分:9)
在一台机器上运行超过40,000个线程的成本是多少?通过让JVM / Linux操作系统管理这么多线程,我失去了多少性能?请记住,当没有工作时,它们都已正确配置为睡眠状态。所以,我只是谈论额外的开销和线程数量导致的问题。
在JVM空间中,每个线程都需要一个线程堆栈(默认为256kb)以及Thread对象和连接的对象。可以使用-Xss选项更改默认线程堆栈,但我相信 64kb是下限。 (40,000 x 256kb是10Gb ......)
在Linux上,每个线程也占用一个OS线程描述符,当线程没有执行时,它将帮助线程的注册上下文...和其他东西。这些描述符是预先分配的,我相信它们不会被分页。这是您的管理员需要增加的资源。
无论线程是清醒还是休眠,都会使用这些资源。
另一个问题是你需要在使用wait / notifyAll进行同步时要小心一些。如果有很多线程在同一个对象上等待,那么当每个线程被唤醒时,notifyAll将引起一连串的活动。 (但是你可以通过没有很多线程等待同一个对象来避免这种情况。)
有关使用大量线程的后果的详细信息,请参阅Oracle Java Threading页面。
我的感觉是40,000个线程过多。理想的线程数与您拥有的物理处理器/核心数成正比。虽然通过拥有大量线程不一定会看到性能下降,但是你会占用大量资源,这可能会产生间接的性能问题;例如更长的GC时间,潜在的虚拟机颠簸。
应用程序的一个更好的体系结构是实现一个线程池和工作队列,以便将工作分配给更少数量的活动线程。
答案 1 :(得分:2)
现在你说线程会在没有工作时睡觉。有多少工作?同时完成了多少工作单元?如果该数量大于处理器数量,并且所述工作主要基于CPU,则实际上会看到整体性能下降。
但我们假设在任何给定时间完成的工作量是处理器的数量。如果是这种情况,我可以看到的第一个问题是将发生的上下文切换量。 Java中的上下文切换(通常是基于)是大约100个指令。如果你所有的线程在很短的时间内被切换(唤醒)来完成他们的一些工作,那么我们正在谈论> 4,000,000条额外说明。
有关上下文切换成本的更多信息,因为它们可能会影响您的程序。此document的摘录解释了切换时验证线程本地缓存的成本
当切换新线程时, 它需要的数据不太可能出现在 本地处理器缓存,所以一个上下文 开关导致一连串的缓存 错过,因此线程运行一点 他们第一次时更慢 调度。这是其中一个原因 调度程序为每个runnable提供 线程一定的最小时间量 即使有许多其他线程 等待
除此之外,您还需要分配额外的堆栈空间,并且还有40,000个线程对象的堆(对于线程,它只有大约7兆的浅堆)。