写点什么

ConcurrentHashMap 性能测试

作者:FunTester
  • 2022 年 6 月 14 日
  • 本文字数:1905 字

    阅读完需:约 6 分钟

之前在测试commons-pool2相关实现的时候,发现在线程接近 500 时候,性能瓶颈降低非常厉害,就好像碰到了总体性能的天花板一样,随着线程继续增加而单线程性能急速下降的现象。当时粗略判断其中一个原因是用来存储对象映射关系的java.util.concurrent.ConcurrentHashMap存在瓶颈导致。


所以今天我特意来测试一下java.util.concurrent.ConcurrentHashMap的查询性能,其他增改的功能暂时不做测试了。关于另外一个可能的原因java.util.concurrent.atomic.AtomicLong,我们下期再测。有兴趣的可以先看看我之前对于更强大的多线程计数器java.util.concurrent.atomic.LongAdder的性能测试:性能测试中的LongAdder。下面是之前遇到两种不同类型的对象池的性能测试文章:通用池化框架GenericObjectPool性能测试通用池化框架GenericKeyedObjectPool性能测试

测试方案

先说一下思路和场景设计。思路还是沿用之前的性能测试,通过固定线程的性能模型进行测试,通过调整次数和线程数来测试java.util.concurrent.ConcurrentHashMap的性能表现。场景设计上我先把java.util.concurrent.ConcurrentHashMap添加 N 个keyvalue,然后通过多线程随机从这些key里面取值。


这样本地测试就有了三个变量线程数次数key的数量,本次重点放在了 200 线程以上的性能表现。


PS:硬件和软件配置参考以前的文章,这里就不多说了。

测试用例

照例方案依旧使用FunTester性能测试框架提供的能力,采取Groovy脚本实现。相信有一定 Java 基础的同学阅读起来是没有问题的。



package com.funtest.groovytest
import com.funtester.base.constaint.FixedThreadimport com.funtester.base.constaint.ThreadBaseimport com.funtester.frame.SourceCodeimport com.funtester.frame.execute.Concurrent
import java.util.concurrent.ConcurrentHashMap
class ConcurrentHashMapTest extends SourceCode {
static ConcurrentHashMap<Integer, Integer> maps = new ConcurrentHashMap<>()
static int times = 1_0000
static int threads = 200
static int num = 100
static def desc = "ConcurrentHashMap性能测试"
public static void main(String[] args) { 1.upto(num) { maps.put(it, it) }
ThreadBase.COUNT = false RUNUP_TIME = 0 new Concurrent(new FunTester(), threads, desc).start() }
private static class FunTester extends FixedThread {

FunTester() { super(null, times, true) }
@Override protected void doing() throws Exception { maps.get(getRandomInt(num)) }
@Override FunTester clone() { return new FunTester() } }
}
复制代码

测试结果

由于测试中基本都触碰到硬件(CPU)瓶颈,所以本次也就不记录 CPU 使用率了,相当于都是在 CPU 资源有限情况下的性能测试数据,其实测试中发现次数影响也不大。



测试到此,结论比较明显了,影响java.util.concurrent.ConcurrentHashMap的主要因素还是机器 CPU 资源不够用了。对于相同的资源情况下,线程数更低自然获得更强的单线程性能,如果增加线程确实可以获取更大的总体 QPS。在key值方面,值越多,QPS 越低。在测试次数上,自然是字数越多,QPS 也大,也符合之前多次测试中的结论。


但是当我重新检查代码的时候却发现一个问题,在com.funtest.groovytest.ConcurrentHashMapTest.FunTester#doing方法中其实还有一段耗时的请求,就是com.funtester.frame.SourceCode#getRandomInt,经过我重新测试,发现java.util.concurrent.ConcurrentHashMap的性能得到了十几倍的提升。


不得不说我大意了,本期文章标题应当修改为java.util.concurrent.ThreadLocalRandom性能测试。


一下是com.funtester.frame.SourceCode#getRandomInt的内容:


    /**     * 获取随机数,获取1~num 的数字,包含 num     *     * @param num 随机数上限     * @return 随机数     */    public static int getRandomInt(int num) {        return ThreadLocalRandom.current().nextInt(num) + 1;    }
复制代码


我依此法重新测试了java.util.concurrent.atomic.AtomicLong,发现也是 QPS 超高,排除了我之前的想法。看来commons-pool2的瓶颈不在这两个地方。以后等我仔细再研究研究,有结论再跟大家分享。

Have Fun ~ Tester !


阅读原文,跳转我的仓库地址

发布于: 刚刚阅读数: 3
用户头像

FunTester

关注

公众号:FunTester,800篇原创,欢迎关注 2020.10.20 加入

Fun·BUG挖掘机·性能征服者·头顶锅盖·Tester

评论

发布
暂无评论
ConcurrentHashMap性能测试_FunTester_InfoQ写作社区