首页 > 其他分享 >推荐一款工具,辅助估算线程池参数

推荐一款工具,辅助估算线程池参数

时间:2022-10-05 14:57:59浏览次数:96  
标签:估算 IO 线程 计算 time gc 一款 CPU

前言

相信接触过并发系统的小伙伴们基本都使用过线程池,或多或少调整过对应的参数。以 Java 中的经典模型来说,能够配置核心线程数、最大线程数、队列容量等等参数。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
  this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
       Executors.defaultThreadFactory(), defaultHandler);
}

一般情况下,我们设置参数步骤是:

  1. 确定业务属性,比如IO密集型、CPU密集型、混合型等。

  2. 参考理想化的线程计算模型算出理论值。如《Java并发编程实战》一书中的理想化模型:

  1. 辅之以压测等手段对参数进行逐步调优。

  2. 再高级点,我们也可以对线程池进行监控,并实时对参数进行调整,也即参数动态化方案。可参考:Java线程池实现原理及其在美团业务中的实践

 

工具推荐

本文则推荐一款工具,它不关心任务内部是如何实现的,而是通过计算运行时的各种系统指标(包括 CPU计算时间、IO等待时间、内存占用等)来直接计算线程池参数的。我们可以直接在这些参数的基础上,再配合压测进行调优,避免盲目调参。

 

这个工具叫做 dark_magic,直译就是黑魔法,源码参见 https://github.com/sunshanpeng/dark_magic。里面的备注已经很详细,本文不再赘述。只提一下系统指标的计算方式。

指标的计算方式

CPU计算时间 和 IO等待时间 的计算:

  • 先执行两遍任务,进行预热。

  • 获取当前线程的 CPU计算时间,记为 C1

  • 再执行一遍任务

  • 获取当前线程的 CPU计算时间,记为 C2

  • 计算当前任务执行需要的 CPU计算时间:C2 - C1

  • 计算当前任务执行中的 IO等待 时间:总耗时 - CPU计算时间

其中,计算当前线程的 CPU计算时间使用 rt.jar 包中的方法:

ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime()

 

内存占用的计算:

  • 生成1000个(可配置)任务加入到阻塞队列中

  • 循环调用 15次(可配置) System.gc() 函数,触发gc

  • 记录目前的内存使用情况,记为 M0

  • 再次生成1000个(可配置)任务加入到阻塞队列中

  • 循环调用 15次(可配置) System.gc() 函数,触发gc

  • 记录目前的内存使用情况,记为 M1

  • 计算当前任务执行需要的内存:M1 - M0

其中,计算内存使用 rt.jar 包中方法:

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()

 

使用方法

该工具的使用方法也很简单:

  1. 把你的业务代码封装为一个函数,放到 createTask 函数中。

  2. 设定 CPU使用率的期望值、队列占用内存的期望值。

  3. 执行,等待结果输出。

 

下面分别展示一个CPU密集型和IO密集型的输出(我们设置的 CPU 使用率期望值为 60%,队列占用内存的期望值为 10MB ):

# CPU密集型

Target queue memory usage (bytes): 10240
createTask() produced threadpool.AsyncCPUTask which took 40 bytes in a queue
Formula: 10240 / 40
* Recommended queue capacity (bytes): 256
Number of CPU: 8
Target utilization: 0.59999999999999997779553950749686919152736663818359375
Elapsed time (nanos): 3000000000
Compute time (nanos): 2949786000
Wait time (nanos): 50214000
Formula: 8 * 0.59999999999999997779553950749686919152736663818359375 * (1 + 50214000 / 2949786000)
* Optimal thread count: 4.79999999999999982236431605997495353221893310546875000

 

# IO密集型

Target queue memory usage (bytes): 10240
createTask() produced threadpool.AsyncIOTask which took 40 bytes in a queue
Formula: 10240 / 40
* Recommended queue capacity (bytes): 256
Number of CPU: 8
Target utilization: 0.59999999999999997779553950749686919152736663818359375
Elapsed time (nanos): 3000000000
Compute time (nanos): 55528000
Wait time (nanos): 2944472000
Formula: 8 * 0.59999999999999997779553950749686919152736663818359375 * (1 + 2944472000 / 55528000)
* Optimal thread count: 259.19999999999999040767306723864749073982238769531250000

 

针对线程数的计算而言:

  • 对于 CPU 密集型任务,IO等待时间(Wait time) 远远小于 CPU计算时间(Compute time)。计算出来的推荐核心线程数为 4.8。

  • 对于 IO 密集型任务,IO等待时间(Wait time) 远远大于 CPU计算时间(Compute time)。计算出来的推荐核心线程数为 259。

 

而队列大小与任务中使用的对象大小有关,这里的内存使用是通过计算 gc 执行前后的内存大小差异得到的(本文中的例子均为 40 B)。由于该算法内部使用 System.gc() 触发 gc。但由于 gc 不一定真的会立刻执行,所以拿到的队列结果可能不一定准确,只能作为粗略参考。

 

总结

总的来说,dark_magic 这款工具以任务执行时的系统指标数据为基础,计算出比较合理的线程池参数,给我们进行后续的压测调参提供了相对比较合理的参考,值得推荐。

 

标签:估算,IO,线程,计算,time,gc,一款,CPU
From: https://www.cnblogs.com/xiaoxi666/p/16755570.html

相关文章

  • 线程之间的通信
    一、为什么要线程通信?  1.多个线程并发执行时,在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务, 并且我们希望他们有规律的执行,那么多线程......
  • 一款很简单的键盘记录器,只保留了基础功能
    //Crack.cpp:定义DLL应用程序的入口点。#include"pch.h"HINSTANCEhin; //模块句柄:即本模块在内存中的首地址BOOLAPIENTRYDllMain(HMODULEhModule, //入口函......
  • 线程控制 fork
    线程控制1.线程控制1.1.并行线程1.2.一些代码示例1.2.1.多个initial并行执行1.2.2.单个initial内串行执行1.2.3.fork...join内并行执行1.2.4.join_any......
  • 线程间同步和通信,event semaphore mailbox
    线程间同步和通信,event semaphore mailbox1.概述2.事件event3.wait_order()4.旗语(semaphore)5.semaphore::get();6.semaphore::try_get()7.信箱mailbox......
  • 线程的控制与同步
    线程的控制与同步1.线程的使用1.1.什么是线程?1.2.线程的概念澄清2.线程的控制2.1.fork并行线程语句块2.2.等待所有衍生线程2.3.停止单个线程2.4.......
  • XX学Python·进程与线程
    多任务编程-进程多任务执行方式并发:在一段时间内交替去执行任务并行:多核cpu每个cpu执行一个任务。注:任务>cpu时,每个cpu并发执行多个任务进程:计算机中的程......
  • 进程和线程区别?
    文章目录进程和线程进程线程进程与线程的区别总结从JVM角度说进程和线程之间的关系(重要)图解进程和线程的关系程序计数器为什么是私有的?虚拟机栈和本......
  • 进程和线程的区别2
    进程和线程的区别 最佳答案进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。还存在资源开销、包含关系、内存分配、影响关系、执行过程等......
  • C++并发编程 [02] :线程管控
    发起线程线程通过构建std::thread对象而启动,该对象指明线程要运行的任务。可以传入任何可调类型给std::thread来构建一个std::thread对象。需要包含头文件<thread......
  • 性能测试线程死锁问题分析和定位【杭州多测师】【杭州多测师_王sir】
    1、死锁的概念:有2个线程、一个线程锁住了资源A、又想去锁定资源B、另外一个线程锁定了资源B、又想去锁定资源A、2个线程都想去得到对方的资源、而又不愿释放自己的的资源......