性能测试误差对比研究(三)

本期内容承接上期性能测试误差对比研究(二)及时上上期性能测试误差对比研究(一),脚本采用与(二)相同,原因不赘述了。今天终于要把坑填完了,想想都有点小兴奋。(PS:其实还有四)

加锁资源

在相对复杂的性能测试场景中,我们往往都会用到线程安全类来达到线程安全的目的,再次我的思路就是,如果有现成的安全类,就采用JDK自带的线程安全类,如果没有合适的线程安全类,我们就需要实现线程安全功能,我一般直接使用synchronized关键字来完成,如果需要初始化的,我全部采用static修饰,加载的时候就初始化完成。

这样做能既可以保证高效地完成用例的编写和调试,还能减少大量因为自己代码能力弱和知识盲区导致的测试不准确甚至测试失败。因为我们准备测试数据可能比较费劲,一旦失败了,会导致一大批测试数据浪费了。

线程安全类

增加一个线程安全类对象属性private static final AtomicInteger fun = new AtomicInteger(),其实在代码中已经引用了一个相同类型的对象来记录总的请求次数。但是影响不够明显。

修改run()方法:

        @Override
        void run() {
            times.times {
                def start = Time.getTimeStamp()
                sleep(0.1)
                100.times {
                    fun.getAndIncrement()
                }
                def end = Time.getTimeStamp()
                excutetimes.getAndIncrement()
                costs.add(end - start)
            }
            countDownLatch.countDown()
        }

这次我依然把时间算在请求耗时中,这里的结论应该和之前日志输出的结论有一半是一致的,就是此处会降低整体的QPS,但是对于两种统计公式误差的影响微乎其微。所以这次我们重点关注对性能的影响,其实也就是测试线程安全的性能如何,当然都是在线程数相对比较低的时候实现的,因为毕竟只是得到结论,只需要知道一个大概的影响趋势即可。

先说一个结论:此类安全类的性能远远超出被测服务的性能的,所以影响不是很大,重点是比较安全类在不同场景下误差影响量化,对以后的测试中使用给出一些建议。

首先使用20线程,50次数,测试结果如下:

INFO-> 当前用户:fv,工作目录:/Users/fv/Documents/workspace/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> 通过平均时间计算QPS:191.4535150865
INFO-> 通过总时间计算QPS:189.7173211914
INFO-> 误差是:0.91%

Process finished with exit code 0

基准测试(删除安全类操作代码),结果如下:

INFO-> 当前用户:fv,工作目录:/Users/fv/Documents/workspace/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> 通过平均时间计算QPS:192.8287006238
INFO-> 通过总时间计算QPS:191.1314984709
INFO-> 误差是:0.88%

Process finished with exit code 0

可以看出QPS影响不是很大,这是在每个循环执行100次安全类操作下的数据。下面我们增加线程数到40,看看测试结果,首先是安全类操作情况下:

INFO-> 当前用户:fv,工作目录:/Users/fv/Documents/workspace/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> 通过平均时间计算QPS:385.0874870635
INFO-> 通过总时间计算QPS:377.9289493575
INFO-> 误差是:1.86%

Process finished with exit code 0

下面是基准测试结果:

INFO-> 当前用户:fv,工作目录:/Users/fv/Documents/workspace/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> 通过平均时间计算QPS:388.4249368809
INFO-> 通过总时间计算QPS:379.6507213364
INFO-> 误差是:2.26%

Process finished with exit code 0

QPS误差变大了一点点,误差变化也不大。结论比较明显了,线程安全类的操作对性能测试结果的影响非常小,大家可以放心使用,哈哈。

synchronized关键字

说完线程安全类,我们还得看一下synchronized关键字,毕竟性能测试用起来真的非常简单,需要掌握的多线程技术并不多。关于性能测试中的多线程技术,我改天找个机会再单独说一说。

改造测试代码,增加属性private static final int fun = 0,增加线程安全方法:

    /**
     * 线程安全方法
     */
    private static void funplus() {
        synchronized (fun) {
            fun ++
        }
    }

与其等价的方法:

    /**
     * 线程安全方法
     */
    private synchronized static void funplus() {
            fun ++
    }

run()方法如下:

        @Override
        void run() {
            times.times {
                def start = Time.getTimeStamp()
                sleep(0.1)
                100.times {
                    funplus()
                }
                def end = Time.getTimeStamp()
                excutetimes.getAndIncrement()
                costs.add(end - start)
            }
            countDownLatch.countDown()
        }

有了线程安全类的实验基础,我们就直接进行20线程50请求的测试,结果如下:

INFO-> 当前用户:fv,工作目录:/Users/fv/Documents/workspace/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> 通过平均时间计算QPS:191.0730663406
INFO-> 通过总时间计算QPS:185.4943424226
INFO-> 误差是:2.92%

Process finished with exit code 0

可以看出,QPS影响依旧不大。再来看一下40线程,50请求次数的测试结果:

INFO-> 当前用户:fv,工作目录:/Users/fv/Documents/workspace/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> 通过平均时间计算QPS:379.6092871412
INFO-> 通过总时间计算QPS:372.9256013425
INFO-> 误差是:1.76%

Process finished with exit code 0

增加请求次数到100,测试结果如下:

INFO-> 当前用户:fv,工作目录:/Users/fv/Documents/workspace/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> 通过平均时间计算QPS:382.9968546383
INFO-> 通过总时间计算QPS:379.0391357908
INFO-> 误差是:1.03%

Process finished with exit code 0

这个结论跟线程安全类差不多,只是粗暴使用synchronized关键字带来的QPS影响更大,但是相比其他误差来源,还是比较小。


FunTester腾讯云年度作者Boss直聘签约作者GDevOps官方合作媒体,非著名测试开发。