• ConcurrentHashMap性能测试


    之前在测试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.FixedThread
    import com.funtester.base.constaint.ThreadBase
    import com.funtester.frame.SourceCode
    import 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资源有限情况下的性能测试数据,其实测试中发现次数影响也不大。

    线程数 次数(千) key数量 单线程QPS
    200 10 100 3038
    200 20 100 3539
    200 40 100 4066
    200 80 100 4334
    200 10 200 2823
    200 20 200 3587
    200 40 200 4736
    200 10 400 2919
    200 10 50 2873
    200 10 20 3218
    200 10 1000 3256
    300 10 100 1893
    300 20 100 2514
    300 40 100 3214
    300 20 300 1798
    300 20 500 2832
    500 20 100 1722
    500 20 1000 1509
    1000 20 1000 816
    1000 10 100 724

    测试到此,结论比较明显了,影响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 !

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

  • 相关阅读:
    使用python和java两种方式来完成下载网页,并保存成文件,
    python 如何把 utf 8 字符串写入文件中_心若止水_百度空间
    如何在linux上架设PPPoE server?
    PHP学习之十三:变量函数
    iPhone开发:iOS Framework制作研究
    iPhone开发:浅析ObjectiveC的动态特性
    Windows Phone 7 开发之Microsoft.Phone.Tasks中的各项Task
    windows phone7 开发 使用系统资源
    Windows Phone 开发之 设备方向
    window phone开发之 WebClient 讲解
  • 原文地址:https://www.cnblogs.com/FunTester/p/16376404.html
Copyright © 2020-2023  润新知