线程池大小设置多少,比较合适?

设置线程数的核心点

压测!压测!再压测!实际对性能要求比较高的场景,压测是最佳的方式!

并发编程适用于什么场景?

CPU 密集型

对于 CPU 密集型任务,希望最大限度地提高 CPU 利用率,但又不会因为过多的线程而压垮系统,否则会导致过多的上下文切换。

使用场景:加密、解密、压缩、计算等一系列需要大量耗费 CPU 资源的任务。

确定线程池大小的公式如下:

线程数 = Ncpu

线程数 = Ncpu + 1

Ncpu:CPU 核数

// 获取CPU内核数量
int number = Runtime.getRuntime().availableProcessors();

对于密集型的任务,应用程序的最小线程数应该等于可用的处理器核数,如果所有任务都是密集型的,处理器的系统通常通过使用 Ncpu + 1 个线程的线程池来获得最优的利用率(计算密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作)。

在这种情况下,创建更多的线程对程序性能而言反而是不利的。因为当有多个任务处于就绪状态时,处理器核心需要在线程间频繁进行上下文切换,而这种切换对程序性能损耗较大。但如果任务被阻塞的时间大于执行时间,即该任务是 I/O 密集型的,我们就需要创建更多的线程来提高性能。

I/O密集型

对于 I/O 密集型任务,最佳线程数通常由 I/O 操作的性质和预期延迟决定。希望有足够的线程来保持 I/O 设备繁忙而不会使它们过载。

使用场景:数据库、文件的读写,网络通信等任务。这种任务的特点是并不会特别消耗 CPU 资源,但是 IO 操作很耗时,总体会占用比较多的时间。

当一个任务执行IO操作时,其线程被阻塞,处理器立即进行上下文切换以便处理其他就绪线程。如果只有处理器可用核心数个线程的话,则即使有待执行的任务也无法处理,因为已经拿不出更多的线程供处理器调度了。

对于包含了 I/O 和其他阻塞操作的任务,因此你需要一个更大的池子。为了正确地设置线程池的长度,你必须估算出任务花在等待的时间与用来计算的时间的比率;这个估算值不必十分精确,而且可以通过一些监控工具获得。你还可以选择另一种方法来调节线程池的大小,在一个基准负载下,使用 几种不同大小的线程池运行你的应用程序,并观察CPU利用率的水平。

公式一

线程数 = Ncpu * Ucpu * (1 + 平均等待时间 / 平均工作时间)

Ucpu:又称 CPU 利用率, 这是应用程序使用 CPU 时间的百分比。取值范围:0 <= Ucpu <= 1

线程的等待时间:线程的等待时间指的是线程在等待某个条件满足或等待其他线程完成时所花费的时间。在等待时间内,线程可能被挂起,不占用 CPU 资源。通常发生在等待某个条件、等待 I/O 操作完成、等待锁释放等情况下。在这段时间内,线程可能不执行任务。

示例:

synchronized (lock) {while (!condition) {try {lock.wait(); // 等待条件满足// 在这里,线程处于等待状态} catch (InterruptedException e) {e.printStackTrace();}}
}
// 在这里,等待完成后线程继续执行工作

线程的工作时间:线程的工作时间指的是线程正在执行任务的时间,也称为 CPU 时间。在这段时间内,线程在处理器上执行指令,执行其分配的任务。

示例:

Thread workerThread = new Thread(() -> {// 线程的工作时间// 执行任务
});workerThread.start();
// 在这里,workerThread 正在工作

通过这个公式,可以计算出一个合理的线程数量,如果任务的平均等待时间长,线程数就随之增加;如果平均工作时间长,也就是上面的 CPU 密集型任务,线程数就随之减少。

假设 Ncpu=12,多个任务运算占总时间的 50%,可以运行的 线程数= 12 x 1 x (1 + 0.5 / 0.5) = 24 个线程。

公式二

线程数 = Ncpu / (1 - 阻塞系数) =  Ncpu * Ucpu = Ncpu * (Ucpu / (1 - 阻塞系数))

阻塞系数: 这是等待时间与服务时间的比率,它衡量线程等待 I/O 操作完成所花费的时间相对于执行计算所花费的时间,阻塞系数的取值在0和1之间(密集型任务的阻塞系数为0,而 I/O 密集型任务的阻塞系数则接近1)。

计算这些类型资源池的大小约束非常简单:首先累加出每一个任务需要的这些资源的总量,然后除以可用的总量,所得的结果是池大小的上限。线程数太少会使得程序整体性能降低,而过多的线程也会消耗内存等其他资源,所以如果想要更准确的话,必须进行压测,并监控 JVM 的线程情况以及 CPU 的负载情况,根据实际情况衡量应该创建的线程数,合理并充分利用资源。

当任务需要使用池化的资源时,比如数据库连接,那么线程池的长度和资源池的长度会相互影响。如果每一个任务都需要一个数据库连接,那么连接池的大小就限制了线程池的有效大小;类似的,当线程池中的任务是连接池的唯一消费者时,那么线程池的大小反而又会限制了连接池的有效大小。

假设 Ncpu=12,多个任务阻塞率是 50%,可以运行的 线程数= 12 / (1 - 0.5) = 24 个线程。

综上所述得出如下结论:

如上所述的估算线程池大小公式:

线程数 = Ncpu /(1 - 阻塞系数)

对于公式一,假设 CPU 100% 运转,撇开 CPU 使用率因素,即:

线程数 = CPU核数 x (1 + 平均等待时间 / 平均工作时间)

现在假设将公式二的公式等于公式一,即

CPU核数 /(1 - 阻塞系数)= CPU核数 x (1 + 平均等待时间 / 平均工作时间)

推导出:

阻塞系数 = 平均等待时间 / (平均等待时间 + 平均工作时间)

阻塞系数 = 阻塞时间 /(阻塞时间 + 计算时间)

如下:

由于对 Web 服务的请求大部分时间都花在等待服务器响应上了,所以阻塞系数会相当高,因此程序需要开的线程数可能是处理器核心数的若干倍。

假设阻塞系数是 0.9,即每个任务 90% 的时间处于阻塞状态而只有 10% 的时间在干活,则在双核处理器上我们就需要开 20 个线程。假若有很多任务要处理的话,一个 8 核处理器上开到 80 个线程来处理该任务。所以:

  • 线程的平均工作时间所占比例越高,就需要越少的线程;
  • 线程的平均等待时间所占比例越高,就需要越多的线程;
  • 针对不同的程序,进行对应的实际测试就可以得到最合适的选择。

实际应用

下面应用中的计算都以 Ncpu = 12 做计算。

实例:假设一台服务器期望应用程序使用 50% 的可用 CPU 资源,怎么设计?

应用程序有两类任务:I/O 密集型任务和 CPU 密集型任务。

I/O 密集型任务的阻塞系数为 0.5,这意味着要花费 50% 的时间等待 I/O 操作完成。

线程数 = Ncpu * 0.5 * (1 + 0.5) = 9

CPU 密集型任务的阻塞系数为 0.1,这意味着要花费 10% 的时间等待 I/O 操作完成。

线程数 = Ncpu * 0.5 * (1 + 0.1) = 7

在此示例中,创建两个线程池,一个用于 I/O 密集型任务,另一个用于 CPU 密集型任务。I/O 密集型线程池将有 9 个线程,CPU 密集型线程池将有 7 个线程。

实例:假设一台服务器平均每秒处理一个事务的时间为 400ms,期望应用程序达到 20TPS(TPS:Transaction Per Second),怎么设计?

为了使应用程序能够达到 20TPS,首先计算出每个事务的平均处理时间,公式如下:

每个事务的平均处理时间(秒)= 每个事务的平均处理时间(毫秒)/ 1000ms

再计算每秒处理的事务量,公式如下:

每秒处理的事务量 = Ncpu * (1 / 每个事务的平均处理时间(秒))

因此,每秒处理事务的数量为:Ncpu * (1 / 0.4) = 12 * 2.5 = 30 个,可见已经高于期望目标了。

实例:假设一台服务器的平均工作时间(非阻塞时间或又称 CPU 调试时间)需要 5ms,DB 平均操作时间(I/O阻塞时间)需要 200ms,怎么设置线程大小?

一个线程的平均执行时间为:5 + 200 = 205ms。

按 CPU 使用率 100% 计算,公式如下:

线程数 = Ncpu * (1 + 平均等待时间 / 平均工作时间)

因此,线程数为: 12 * (1 + 200 / 5) = 492 个。

实例:结合上个实例,如果 DB 的上限是 1000QPS(Query Per Second),此时又该如何设置这个线程大小?

首先将每个查询的平均处理时间转换为秒,公式如下:

每个查询的平均处理时间(秒)= 每个查询的平均处理时间(毫秒)/ 1000ms

再计算每秒处理的查询量,公式如下:

每秒处理的查询量 = Ncpu * (1 / 每个查询的平均处理时间(秒))

因此,每秒处理的查询量为:Ncpu * (1 / 0.205) = 12 * 4.87 = 58.536 个。

那么如果使用上个实例算出的线程数(492),则每秒可处理的查询数量的公式如下:

每秒可处理的查询数量 = 线程数 * (1 / 每个查询的平均处理时间(秒))

因此,每秒可处理查询的数量是:492 * 1000 / 205 = 2400QPS。

此时每秒可处理查询的数量已经大于了 DB 的上限,所以线程数就要等比例减少,公式为如下:

新线程数 = 线程数 * (DB 的上限 / 每秒可处理的查询数量)

因此,新线程数为:492 * 1000 / 2400 = 205 个。
 

实例:假设一台服务器平均每秒处理一个查询的时间为 200ms,且这些查询没有 CPU 阻塞,期望应用程序达到 1000QPS(QPS:Query Per Second),怎么设计?

注意:需求已经查询表明 CPU 无阻塞的问题,可以理解为 CPU 密集型,所以就就意味着:线程数=Ncpu(如果为防止线程意外停止即:线程数 = Ncpu + 1)。

首先先将每个查询的平均处理时间转换为秒,公式如下:

每个查询的平均处理时间(秒)= 每个查询的平均处理时间(毫秒)/ 1000ms

再计算每秒处理的查询量,公式如下:

每秒处理的查询量 = Ncpu * (1 / 每个查询的平均处理时间(秒))

因此,每秒处理查询的数量为:Ncpu * (1 / 0.2) = 12 * 5 = 60 个。

由此可以看见 1000QPS 指标与单台服务器 60 个查询线程之间的差距,要想达到期望指标仅靠一台服务器是远远达不到的,这时就需要扩容服务器来达到所期望目标。

为了计算需要扩容多少台服务器,公式如下:

总服务器数量 = 每秒总查询量 / 每秒处理的查询量

因此,要想达到为 1000QPS 的指标需要 1000 / 60 = 17 台服务器才能达成目标。

实例:假设一台服务器平均每秒处理一个查询的时间为 200ms,且查询时间占总时间的 90%,期望应用程序达到 1000QPS(QPS:Query Per Second),怎么设计?

首先计算每个查询的实际处理时间,考虑到这些查询占总时间的90%,公式如下:

每个查询的实际处理时间 = 每个查询的平均处理时间 / 占总时间的比例 

因此,每个查询的实际处理时间为:200ms / 0.9 = 222.22ms

再计算每秒处理的查询量,公式如下:

每秒处理的查询量 = Ncpu * (1 / 每个查询的实际处理时间(秒))

因些,每秒处理的查询量为:Ncpu * (1 / 0.222) = 12 * 4.5 = 54 个查询。由此可以看见 1000QPS 指标与单台服务器 54 个查询线程之间的差距,要想达到期望指标仅靠一台服务器是远远达不到的,这时就需要扩容服务器来达到所期望目标。

为了计算需要扩容多少台服务器,公式如下:

总服务器数量 = 每秒总查询量 / 每秒处理的查询量

因此,要想达到为 1000QPS 的指标需要 1000 / 54 = 19 台服务器才能达成目标。

阿姆达尔定律(Amdahl)

根据上面的信息得出一个想法:如果增加 N 个 CPU 核数后性能是不是会嗖嗖嗖往上嗖?先不做问题解答,因为想要弄明白这个需要先弄明白 阿姆达尔定律(Amdahl) 是什么才能更好的理解。

什么是阿姆达尔定律(Amdahl)?

旨在用公式描述在并行计算中,多核处理器理论上能够提高多少倍速度。它代表了处理器并行运算之后效率提升的能力。

公式如下:

S(N)=\frac{1}{1-P+\frac{P}{N}}

说明:

  • S(N):是加速比(Speedup)
  • P:是并行化部分占总程序执行时间的比例
  • N:是并行化部分的处理器数目

加速比(Speedup enhanced

系统原来串行计算需要 6s,加速后只需要 3s,即:S。

计算公式如下:

加速比 = 原有运行时间(顺序执行的时间) / 并行计算加速后的时间 

因为,加速比为:6 / 3 = 2,由此可知加速比的值永远大于1。

部分提高(Fraction enhanced

部分提高是并行化部分占总程序执行时间的比例,即:P。

假若程序总共有 100 行代码,其中 50 行是可以通过并行计算的,那么这 50 行代码就是部分提高。但是实际上部分提高是一个比例数值,是并行计算代码 / 总代码量。

公式如下:

部分提高 = 串行代码 / 总代码量

因此,部分提高为:50 / 100 = 0.5,由此可见部分提高的值永远小于 1。

带入 阿姆达尔定律

分别把 部分提高 和 加速比增强 带入阿姆达尔定律中。部分提高 对应公式中的,即并行计算所占比例。加速比增强 对应,即并行节点处理个数。

加速比增强 为什么可以代替 Ncpu ?

想必这里可能有一点疑问,为什么 加速比增强 = 未加速前时间 / 加速后的时间,为什么就可以代表并行节点处理个数?

在理论上,单核处理器处理一个任务需要 200ms,那么双核处理它应该需要 100ms。时间上提速了2倍, CPU 个数上也提升了 2 倍,故两个可以替换。

总结

P 为并行计算所占比例,N 为并行节点处理个数。

当 1 - P = 0 时,没有串行,只有并行,最大加速比 S(N) = N;当 P = 0 时,只有串行,没有并行,最小加速比 S(N) = 1;当 cpu 核心数无限增多的时候,极限加速比 S(N) = 1 / (1 - P),这也就是加速比的上限。

由此我们可知,在并行系统中一味的增加运算资源,并不能永远成倍的提升系统整体性能。

实际应用

实例:假设查询操作占整体系统运行流程比例的 50%(P=0.5),并且为了保持 1000QPS,想要计算提升比例?

可以考虑增加并行处理的处理器数目,即 N。

公式如下:

S(N)=\frac{1}{1-0.5+\frac{0.5}{N}}

现在,我们可以通过计算不同 N 值下的加速比,找到一个合适的 N 值以实现整体性能提升。例如,当 N=12 时,公式如下:

S(12)=\frac{1}{1-0.5+\frac{0.5}{12}}=\frac{1}{0.5+0.0417}=1.8460

以上结果说明即使保证了 1000QPS,整体提升比例仍不超过2倍。

实例:假设查询操作占整体系统运行流程比例的 90%(P=0.9),并且为了保持 1000QPS,想要计算提升比例?

可以考虑增加并行处理的处理器数目,即 N。

公式如下:

S(N)=\frac{1}{1-0.9+\frac{0.9}{N}}

现在,我们可以通过计算不同 N 值下的加速比,找到一个合适的 N 值以实现整体性能提升。例如,当 N=12 时,公式如下:

S(12)=\frac{1}{1-0.9+\frac{0.9}{12}}=\frac{1}{0.1+0.0083}=9.2334

以上结果说明即使保证了 1000QPS,整体提升比例可以达到近10 倍。

通过计算不同 N 值下的加速比,你可以看到整体系统的性能提升。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/209339.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

每周一算法:背包问题(三)多重背包

多重背包 有 N N N件物品和一个容量是 M M M的背包。第 i i i种物品最多有 s i s_i si​件&#xff0c;每件的体积是 v i v_i vi​&#xff0c;价值是 w i w_i wi​。 求解将哪些物品装入背包&#xff0c;可使这些物品的总体积不超过背包容量&#xff0c;且总价值最大。 输…

pytorch 模型量化quantization

pytorch 模型量化quantization 1.workflow1.1 PTQ1.2 QAT 2. demo2.1 构建resnet101_quantization模型2.2 PTQ2.3 QAT 参考文献 pytorch框架提供了三种量化方法&#xff0c;包括&#xff1a; Dynamic QuantizationPost-Training Static Quantization&#xff08;PTQ&#xff0…

DevOps搭建(三)-Git安装详细步骤

前面两篇文章我们讲了如何安装swappiness安装和虚拟机。这篇我们详细讲下如何安装Git。 1、YUM源更改为阿里云镜像源 1.1、备份CentOS-Base.repo 先备份原有的 CentOS-Base.repo 文件 sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup…

OCR原理解析

目录 1.概述 2.应用场景 3.发展历史 4.基于传统算法的OCR技术原理 4.1 图像预处理 4.1.1 灰度化 4.1.2 二值化 4.1.3 去噪 4.1.4 倾斜检测与校正 4.1.4.2 轮廓矫正 4.1.5 透视矫正 4.2 版面分析 4.2.1 连通域检测文本 4.2.2 MSER检测文本 4.3 字符切割 4.3.1 连…

2022年全国大学生数据分析大赛医药电商销售数据分析求解全过程论文及程序

2022年全国大学生数据分析大赛 医药电商销售数据分析 原题再现&#xff1a; 问题背景   20 世纪 90 年代是电子数据交换时代&#xff0c;中国电子商务开始起步并初见雏形&#xff0c;随后 Web 技术爆炸式成长使电子商务处于蓬勃发展阶段&#xff0c;目前互联网信息碎片化以…

数组逆序重放

数组逆序重放的意思是将数组的元素逆序排列&#xff0c;然后重新放回原数组中。这个操作可以在很多编程语言中实现&#xff0c;例如Python、Java等。 下面是一个Python的示例代码&#xff0c;可以实现这个操作&#xff1a; def reverse_and_rearrange(arr): # 反转数组 …

git rebase冲突说明(base\remote\local概念说明)

主线日志及修改 $ git log master -p commit 31213fad6150b9899c7e6b27b245aaa69d2fdcff (master) Author: Date: Tue Nov 28 10:19:53 2023 08004diff --git a/123.txt b/123.txt index 294d779..a712711 100644 --- a/123.txtb/123.txt-1,3 1,4 123 4^Mcommit a77b518156…

分享几个电视颜色测试图形卡

介绍 本文分享几个常见的电视颜色测试图形卡和一段matlab程序&#xff0c;完成JPG转FPGA烧写文件&#xff0c;便于把彩色图片预装载到FPGA内。 电视颜色测试图形卡 一种专业检测电视显示效果的工具。它通常由一张卡片和一些色块组成&#xff0c;可以根据标准色彩空间和颜色渐…

Web安全漏洞分析-XSS(中)

随着互联网的迅猛发展&#xff0c;Web应用的普及程度也愈发广泛。然而&#xff0c;随之而来的是各种安全威胁的不断涌现&#xff0c;其中最为常见而危险的之一就是跨站脚本攻击&#xff08;Cross-Site Scripting&#xff0c;简称XSS&#xff09;。XSS攻击一直以来都是Web安全领…

版本依赖冲突问题排查过程记录

问题 开发平台在集成minio时&#xff0c;pom引入了sdk。 <dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.7</version> </dependency>在调用上传文件API时&#xff0c;控制台报错&…

如何开启Windows Server 2016 远端桌面

使用GUI 设定 服务器管理器–> 本地服务器–> 远端桌面 启用远端桌面 远端–> 允许远端连线至此电脑 会提示防火墙设定跟电源设定 防火墙之前已经关闭了 完成

fpga rom 初始化文件的一些心得

目录 可能遇到的问题 问题 解决方案 rom的初始化 用途 文件类型 如何生成初始化文件 示例 Altera Xilinx 可能遇到的问题 问题 altera FPGA的rom找不到初始化文件&#xff0c;编译过程会提示类似的问题 Error(127001): Cant find Memory Initialization File or He…

Google Earth Engine谷歌地球引擎计算多年中某两个时间点之间遥感数据差值的平均值

本文介绍在谷歌地球引擎GEE中&#xff0c;提取、计算某一种遥感影像产品在连续的多年中&#xff0c;2个不同时相的数据差值的多年平均值&#xff0c;并将计算得到的这一景差值的结果图像导出的方法。 本文是谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#x…

R语言单因素方差分析+差异显著字母法标注+逐行详细解释

R语言单因素方差分析 代码如下 df <- read.csv("data.csv",header TRUE,row.names 1) library(reshape2) df <- melt(df,idc()) names(df) <- c(trt, val) df aov1 <- aov(val~trt,datadf) summary(aov1)library(agricolae) data <- LSD.test(aov…

harmonyOS学习笔记之stateStyles

stateStyles:多态样式 stateStyles可以依据组件的内部状态的不同,设置不同的样式 stateStyles是属性方法,可以根据状态来设置样式,类似于css伪类,但是语法不一样,ArkUI提供了四种状态: focused:获焦态 normal:正常态 pressed:按压态 disable:不可用态例如: Entry Component …

NAND Flash和NOR Flash的异同

NAND Flash和NOR Flash是两种常见的闪存类型。 NOR Flash是Intel于1988年首先开发出来的存储技术&#xff0c;改变了原先由EPROM和EEPROM一统天下的局面。 NAND Flash是东芝公司于1989年发布的存储结构&#xff0c;强调降低每比特的成本&#xff0c;更高的性能&#xff0c;并…

java企业财务管理系统springboot+jsp

1、基本内容 &#xff08;1&#xff09;搭建基础环境&#xff0c;下载JDK、开发工具eclipse/idea。 &#xff08;2&#xff09;通过HTML/CSS/JS搭建前端框架。 &#xff08;3&#xff09;下载MySql数据库&#xff0c;设计数据库表&#xff0c;用于存储系统数据。 &#xff08;4…

LeedCode刷题---子数组问题

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C/C》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、最大子数组和 题目链接&#xff1a;最大子数组和 题目描述 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连…

【计算机组成原理】存储器知识

目录 1、存储器分类 1.1、按存储介质分类 1.2、按存取方式分类 1.3、按信息的可改写性分类 1.4、按信息的可保存性分类 1.5、按功能和存取速度分类 2、存储器技术指标 2.1、存储容量 2.2、存取速度 3、存储系统层次结构 4、主存的基本结构 5、主存中数据的存放 5.…

浅学指针(5)sizeof和strlen的进阶理解

系列文章目录 文章目录 系列文章目录前言1. sizeof和strlen的对⽐1.1 sizeofsizeof不是函数&#xff0c;是运算符 1.2 strlen1.3 sizeof 和 strlen的对⽐ 2. 数组和指针笔试题解析• sizeof(数组名)&#xff0c;sizeof中单独放数组名&#xff0c;这⾥的数组名表⽰整个数组&…