当前位置:   article > 正文

[线程池]CPU密集型和IO密集型对 CPU内核之间的关系_io密集型和cpu密集型线程池数

io密集型和cpu密集型线程池数

[线程池]CPU密集型和IO密集型对 CPU内核之间的关系

引言

​ 在开发过程中,经常要使用到线程池,然而在自定义线程池的过程中,核心线程数应该如何设置呢?一般网上的答案是,根据IO密集型和CPU密集型进行区别设置

  • CPU 密集型的程序 - 线程数 = CPU核心数 + 1
  • I/O 密集型的程序 - 线程数 = CPU核心数 * 2

先不说实际运用中是否按照以上公式设置,我们先来看一下什么是IO密集型程序和CPU密集型程序。

IO密集型程序

​ IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作大部分的状况是CPU在等IO (硬盘/内存) 的读写操作,因此,CPU负载并不高。

​ 密集型的程序一般在达到性能极限时,CPU占用率仍然较低。这可能是因为任务本身需要大量I/O操作,而程序的逻辑做得不是很好,没有充分利用处理器能力。导致CPU 使用率较低,程序中存在大量的 I/O 操作占用时间,线程空余时间很多,通常需要开出CPU核心数数倍的线程。

CPU密集型程序

CPU密集型也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要好很多,此时,系统运作CPU读写IO(硬盘/内存)时,IO可以在很短的时间内完成,而CPU还有许多运算要处理,因此,CPU负载很高。

CPU密集表示该任务需要大量的运算,而没有阻塞,CPU一直全速运行。CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程),而在单核CPU上,无论你开几个模拟的多线程该任务都不可能得到加速,因为CPU总的运算能力就只有这么多。

CPU使用率较高(例如:计算圆周率、对视频进行高清解码、矩阵运算等情况)的情况下,通常,线程数只需要设置为CPU核心数的线程个数就可以了。 这一情况多出现在一些业务复杂的计算和逻辑处理过程中。比如说,现在的一些机器学习和深度学习的模型训练和推理任务,包含了大量的矩阵运算。

小结

  1. 一个极端的线程(不停执行“计算”型操作时),就可以把单个核心的利用率跑满,此时,多核心CPU最多只能同时执行等于核心数的“极端”线程数

  2. 如果每个线程都这么“极端”,且同时执行的线程数超过核心数,会导致不必要的切换,造成负载过高,只会让执行更慢

  3. I/O 等暂停类操作时,CPU处于空闲状态,操作系统调度CPU执行其他线程,可以提高CPU利用率,此时可以同时执行更多的线程

  4. I/O 事件的频率频率越高,或者等待/暂停时间越长,CPU的空闲时间也就更长,利用率越低,操作系统就可以调度CPU执行更多的线程来提高利用率。

可以看到,实际线程到底多少是由CPU计算时间、等待时间(IO操作)、CPU核心数量、开发人员期望的CPU使用效率所决定的,下面引入更加细化的公式。

线程数规划的公式

《Java 并发编程实战》介绍了一个线程数计算的公式:

CleanShot 2023-09-07 at 12.41.41@2x.png

如果希望程序跑到CPU的目标利用率,需要的线程数公式为:

CleanShot 2023-09-07 at 12.42.02@2x.png

虽然公式很好,但在真实的程序中,一般很难获得准确的等待时间和计算时间,因为程序很复杂,不只是“计算” 。一段代码中会有很多的内存读写,计算,I/O 等复合操作,精确的获取这两个指标很难,所以光靠公式计算线程数过于理想化。

真实程序中的线程数

​ 那么在实际的程序中,或者说一些Java的业务系统中,线程数(线程池大小)规划多少合适呢?

先说结论:没有固定答案,先设定预期,比如我期望的CPU利用率在多少,负载在多少,GC频率多少之类的指标后,再通过测试不断的调整到一个合理的线程数

​ 比如一个普通的,SpringBoot 为基础的业务系统,默认Tomcat容器+HikariCP连接池+G1回收器,如果此时项目中也需要一个业务场景的多线程(或者线程池)来异步/并行执行业务流程。

​ 此时我按照上面的公式来规划线程数的话,误差一定会很大。因为此时这台主机上,已经有很多运行中的线程了,Tomcat有自己的线程池,HikariCP也有自己的后台线程,JVM也有一些编译的线程,连G1都有自己的后台线程。这些线程也是运行在当前进程、当前主机上的,也会占用CPU的资源。

所以受环境干扰下,单靠公式很难准确的规划线程数,一定要通过测试来验证。

流程一般是这样:

  1. 分析当前主机上,有没有其他进程干扰
  2. 分析当前JVM进程上,有没有其他运行中或可能运行的线程
  3. 设定目标
    1. 目标CPU利用率 - 我最高能容忍我的CPU飙到多少?
    2. 目标GC频率/暂停时间 - 多线程执行后,GC频率会增高,最大能容忍到什么频率,每次暂停时间多少?
    3. 执行效率 - 比如批处理时,我单位时间内要开多少线程才能及时处理完毕
    4. ……
  4. 梳理链路关键点,是否有卡脖子的点,因为如果线程数过多,链路上某些节点资源有限可能会导致大量的线程在等待资源(比如三方接口限流,连接池数量有限,中间件压力过大无法支撑等)
  5. 不断的增加/减少线程数来测试,按最高的要求去测试,最终获得一个“满足要求”的线程数**

而且而且而且!不同场景下的线程数理念也有所不同:

  1. Tomcat中的maxThreads,在Blocking I/O和No-Blocking I/O下就不一样
  2. Dubbo 默认还是单连接呢,也有I/O线程(池)和业务线程(池)的区分,I/O线程一般不是瓶颈,所以不必太多,但业务线程很容易称为瓶颈
  3. Redis 6.0以后也是多线程了,不过它只是I/O 多线程,“业务”处理还是单线程

所以,不要纠结设置多少线程了。没有标准答案,一定要结合场景,带着目标,通过测试去找到一个最合适的线程数。

对于很多的内部业务系统,并不需要啥性能,稳定好用符合需求就可以那么推荐的线程数是:CPU核心数

获取核心线程说的方法

1. Java中 获取CPU核心数

Runtime.getRuntime().availableProcessors()//获取逻辑核心数,如6核心12线程,那么返回的是12
  • 1

2. Linux 获取CPU核心数

# 总核数 = 物理CPU个数 X 每颗物理CPU的核数 
# 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数

# 查看物理CPU个数
cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l

# 查看每个物理CPU中core的个数(即核数)
cat /proc/cpuinfo| grep "cpu cores"| uniq

# 查看逻辑CPU的个数
cat /proc/cpuinfo| grep "processor"| wc -l
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

文章引用

链接:https://juejin.cn/post/7027610930431131685
链接:https://juejin.cn/post/7280429214608146490

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/952306
推荐阅读
相关标签
  

闽ICP备14008679号