推荐一款工具,辅助估算线程池参数
前言
相信接触过并发系统的小伙伴们基本都使用过线程池,或多或少调整过对应的参数。以 Java 中的经典模型来说,能够配置核心线程数、最大线程数、队列容量等等参数。
一般情况下,我们设置参数步骤是:
确定业务属性,比如 IO 密集型、CPU 密集型、混合型等。
参考理想化的线程计算模型算出理论值。如《Java 并发编程实战》一书中的理想化模型:
辅之以压测等手段对参数进行逐步调优。
再高级点,我们也可以对线程池进行监控,并实时对参数进行调整,也即参数动态化方案。可参考: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 包中的方法:
内存占用的计算:
生成 1000 个(可配置)任务加入到阻塞队列中
循环调用 15 次(可配置) System.gc() 函数,触发 gc
记录目前的内存使用情况,记为 M0
再次生成 1000 个(可配置)任务加入到阻塞队列中
循环调用 15 次(可配置) System.gc() 函数,触发 gc
记录目前的内存使用情况,记为 M1
计算当前任务执行需要的内存:M1 - M0
其中,计算内存使用 rt.jar 包中方法:
使用方法
该工具的使用方法也很简单:
把你的业务代码封装为一个函数,放到 createTask 函数中。
设定 CPU 使用率的期望值、队列占用内存的期望值。
执行,等待结果输出。
下面分别展示一个 CPU 密集型和 IO 密集型的输出(我们设置的 CPU 使用率期望值为 60%,队列占用内存的期望值为 10MB ):
针对线程数的计算而言:
对于 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 这款工具以任务执行时的系统指标数据为基础,计算出比较合理的线程池参数,给我们进行后续的压测调参提供了相对比较合理的参考,值得推荐。
版权声明: 本文为 InfoQ 作者【xiaoxi666】的原创文章。
原文链接:【http://xie.infoq.cn/article/4dcf26970e6d2ac82e99a0650】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论