• 性能测试中的随机数性能问题探索


    在软件测试中,经常会遇到随机数。我简单分成了两类:

    1. 简单取随机数;
    2. 从一个集合中随机取值。

    其实第二个场景包含在第一个场景内。对于接口测试来说,通常我们直接使用第二种场景比较多,就是从某一个集合中随机取一个值。如果更复杂一些,每个值拥有不同的权重,其中这个也可以转化成第二个场景来说。

    缘起

    为什么要把第二个场景和第一个场景分开呢,这个问题源于之前写过的文章ConcurrentHashMap性能测试,当时发现自己封装的com.funtester.frame.SourceCode#random(java.util.List<F>)方法性能存在瓶颈,特别消耗CPU资源。

    虽然单机QPS也在50万+,但是因为这个方法很多地方都会用到,所以还是想提升一些性能。所以我就搜索了一些高性能随机数的功能,跟我之前搜到的资料一致,使用java.util.concurrent.ThreadLocalRandom这个实现类是性能最高的,方法如下:

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

    针对第二种场景,还有一种实现思路:通过循环去集合中取即可。就是顺序去取,而不是每次都从集合中随机。

    举个例子,我们有10万测试用户进行流量回放,演示代码如下:

            def funtest = {
                random(drivers).getGetResponse(random(urls))
            }
            new FunQpsConcurrent(funtest).start()
    
    

    这里调用了两次com.funtester.frame.SourceCode#random(java.util.List<F>),当QPS到达10万级别时候,理论上这个方法导致的瓶颈还是有一些影响的。

    多线程

    所以我用了新思路进行改造,下面是两种思路的对比压测用例,这个测试用例里面其实有三个实现:

    1. random
    2. AtomicInteger
    3. int

    用例如下:

    package com.funtest.groovytest
    
    import com.funtester.base.constaint.FixedThread
    import com.funtester.frame.SourceCode
    import com.funtester.frame.execute.Concurrent
    
    import java.util.concurrent.atomic.AtomicInteger
    
    class FunTest extends SourceCode {
    
        static int times = 1000
    
        static int thread = 500
    
        static def integers = 0..100 as List
    
        static def integer = new AtomicInteger()
    
        static def i = 0
    
        static def size = integers.size()
    
        public static void main(String[] args) {
            RUNUP_TIME = 0
            new Concurrent(new FunTester(), thread, "测试随机数性能").start()
        }
    
        private static class FunTester extends FixedThread {
    
    
            FunTester() {
                super(null, times, true)
            }
    
            @Override
            protected void doing() throws Exception {
                            10000.times {random(integers)}
    //            10000.times {integers.get(integer.getAndIncrement() % size)}
    //                        10000.times {integers.get(i++ % size)}
            }
    
            @Override
            FunTester clone() {
                return new FunTester()
            }
        }
    
    }
    

    由于测试中均达到了CPU硬件瓶颈,相同参数情况下结论比较明显,就没有进行多轮的对比测试。下面分享一下测试结果:

    1. random:1151
    2. AtomicInteger:3152
    3. int:2273

    没想到用了java.util.concurrent.atomic.AtomicInteger反而性能更高了,这个问题略微有点深奥,暂时没有思路。

    单线程

    下面我们来测试一下单线程的性能,下面是我的用例:

    package com.funtest.groovytest
    
    
    import com.funtester.frame.SourceCode
    
    import java.util.concurrent.atomic.AtomicInteger
    
    class FunTestT extends SourceCode {
    
        static int times = 1000000
    
        static def integers = 0..100 as List
    
        static def integer = new AtomicInteger()
    
        static def i = 0
    
        static def size = integers.size()
    
        public static void main(String[] args) {
    
            time {
                //            times.times {random(integers)}
                //                        times.times {integers.get(integer.getAndIncrement() % size)}
                times.times {integers.get(i++ % size)}
            } , "随机数性能测试"
    
        }
    
    
    }
    

    下面是测试结果,这里我记录了执行完所有循环次数的时间,单位是ms(毫秒)。

    1. random:763
    2. AtomicInteger:207
    3. int:270

    这下结论明确了,就java.util.concurrent.atomic.AtomicInteger了。

    末了

    最终写了一个新的随机对象的方法:

        /**
         * 随机选择某个对象
         *
         * @param list
         * @param index 自增索引
         * @param <F>
         * @return
         */
        public static <F> F random(List<F> list, AtomicInteger index) {
            if (list == null || list.isEmpty()) ParamException.fail("数组不能为空!");
            return list.get(index.getAndIncrement() % list.size());
        }
    

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

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

  • 相关阅读:
    pom.xml
    mongo 根据时间范围查找
    nodejs. cron风,定时任务时间写法
    Linux操作命令(一)
    WPF的ScrollViewer鼠标的滚动
    WPF中Expander控件样式,ListBox的样式(带checkbox)恢复
    CentOS7 安装RabbitMQ
    maven项目中配置jdk1.8插件
    赋予其他用户远程连接自己数据库的权限
    递归获取XML文件中的所有节点
  • 原文地址:https://www.cnblogs.com/FunTester/p/16470221.html
Copyright © 2020-2023  润新知