• 性能测试中的LongAdder


    在性能测试中,其本质基础就是多线程编程。这其中多线程安全计数是一个很常见的使用场景,在此之前我用的是java.util.concurrent.atomic.AtomicLong或者java.util.concurrent.atomic.AtomicInteger,但是在最近深入学习多线程编程的过程中(随着线程数增加),发现知识又需要增加了。

    随着java.util.concurrent.atomic.LongAdder来到视野中,下面分享一下基础使用和性能测试。

    简介

    LongAdder中会维护一组变量,这些变量加起来就是要以原子方式更新的long型变量。当更新方法add(long)在线程间竞争时,该组变量可以动态增长以减缓竞争。方法sum()返回当前在维持总和的变量上的总和。与AtomicLong相比,在低并发环境下,两者性能很相似。但在高并发环境下,LongAdder有着明显更高的吞吐量,但是有着更高的空间复杂度(就是内存占用偏高)。LongAdderJDK1.8开始出现的,所提供的API基本上可以替换掉原先的AtomicLong

    基础方法

    对于计数器功能来讲,常用的功能无法三种:构造方法、计数方法(增减)、统计方法(获取值)。

    构造方法

    java.util.concurrent.atomic.LongAdder只有一个构造方法:

        /**
         * Creates a new adder with initial sum of zero.
         */
        public LongAdder() {
        }
    

    默认值0

    计数方法

    这里java.util.concurrent.atomic.LongAdder提供的四种:增减1,增减任意值。都比较简单。简单看一下源码即可上手。这里分享一个重置方法。

        /**
         * Resets variables maintaining the sum to zero.  This method may
         * be a useful alternative to creating a new adder, but is only
         * effective if there are no concurrent updates.  Because this
         * method is intrinsically racy, it should only be used when it is
         * known that no threads are concurrently updating.
         */
        public void reset() {
            Cell[] as = cells; Cell a;
            base = 0L;
            if (as != null) {
                for (int i = 0; i < as.length; ++i) {
                    if ((a = as[i]) != null)
                        a.value = 0L;
                }
            }
        }
    

    由于设计思路和运行机制,这个reset()并不常用,如果是到达某个值就reset()重新计数,大多数情况下并不能符合预期。包括后面还有一个java.util.concurrent.atomic.LongAdder#sumThenReset方法也是一样的,不过可以做一个定时获取存量重新计数的功能,说白点,就是QPS取样器。

    统计

    统计方法中最核心的是java.util.concurrent.atomic.LongAdder#sum,其他java.util.concurrent.atomic.LongAdder#intValue或者java.util.concurrent.atomic.LongAdder#longValue都是基于此方法。然后把值转成其他类型返回。

        /**
         * Returns the current sum.  The returned value is <em>NOT</em> an
         * atomic snapshot; invocation in the absence of concurrent
         * updates returns an accurate result, but concurrent updates that
         * occur while the sum is being calculated might not be
         * incorporated.
         *
         * @return the sum
         */
        public long sum() {
            Cell[] as = cells; Cell a;
            long sum = base;
            if (as != null) {
                for (int i = 0; i < as.length; ++i) {
                    if ((a = as[i]) != null)
                        sum += a.value;
                }
            }
            return sum;
        }
    

    同样的道理,这个方法在性能测试中,自然也不是那么实时。这个问题需要避免。

    性能测试

    下面写个简单的性能测试脚本:

        public static void main(String[] args) {
            LongAdder adder = new LongAdder()
            POOL_SIZE = 1000
            def phaser = new Phaser(1)
            time {
                POOL_SIZE.times {
                    fun {
                        1000000.times {
                            adder.increment()
                        }
                    } , phaser
                }
                phaser.arriveAndAwaitAdvance()
            }
            output(formatLong(adder.longValue()))
        }
    
    

    我的测试方案是固定1000个线程,调整单线程次数。下面是测试结果:

    INFO-> 34.738 main 执行1次耗时:11,088 ms
    INFO-> 34.739 main 1,000,000,000
    
    INFO-> 19.588 main 执行1次耗时:1,194 ms
    INFO-> 19.588 main 100,000,000
    
    INFO-> 07.095 main 执行1次耗时:441 ms
    INFO-> 07.096 main 10,000,000
    

    下面是java.util.concurrent.atomic.AtomicLong的测试结果:

    INFO-> 29.627 main 执行1次耗时:30,570 ms
    INFO-> 29.628 main 1,000,000,000
    
    INFO-> 45.557 main 执行1次耗时:4,600 ms
    INFO-> 45.558 main 100,000,000
    
    INFO-> 22.594 main 执行1次耗时:633 ms
    INFO-> 22.595 main 10,000,000
    

    对比之下,果断换了。PS:LongAdder只有在竞争比较多的时候性能比AtomicLong性能好很多。除此以外,还是老老实实用旧的。

    Have Fun ~ Tester !

  • 相关阅读:
    组合模式/composite模式/对象结构型模式
    迭代器模式/iterator模式/对象行为型模式
    复制Eclipse工作空间设置
    各种命令
    Java相关框架
    基于hk2框架的功能测试Mock注入
    个人项目----词频统计(补全功能)
    小组项目----用户需求调查
    四人小组项目申请
    补第一周“四人小组项目“
  • 原文地址:https://www.cnblogs.com/FunTester/p/16068837.html
Copyright © 2020-2023  润新知