之前写过了- 通用池化框架 commons-pool2 实践以及通用池化框架实践之 GenericKeyedObjectPool。接下来我就对这个池化框架进行性能测试。首先呢就是因为这个池化技术必需要有足够的性能,不然通过池化技术优化的部分,在较高 QPS 的性能测试中,对象池可能成为本机瓶颈。

硬件软件配置

硬件就是我自己的电脑,型号MacBook Pro (16-inch, 2019),配置6C16G。因为这次测试并没有测试到性能极限,在测试方案设计的前阶段已经有了相对明显的结论了。

软件方面,还是 Groovy,默认 Java 进程启动参数。对象池化的设置后面可以在代码中看到,经过我的测试,只要对象池中还有空闲对象就足够满足当前性能。因为我本次用的是固定线程模型,所以换算过来就是对象数大于线程数即可。

测试前准备

测试分成了两部分:无等待归还对象、有等待归还对象。因为无等待归还对象测试过程中,结论出现的太早也太明显了。

可池化对象

/**
 * 可池化对象
 */
private static class FunTesterPooled {

    String name

    int age

}

池化工场

/**
 * 池化工厂
 */
private static class FunFactory extends BasePooledObjectFactory<FunTesterPooled> {


    @Override
    FunTesterPooled create() throws Exception {
        return new FunTesterPooled()
    }

    @Override
    PooledObject<FunTesterPooled> wrap(FunTesterPooled obj) {
        return new DefaultPooledObject<FunTesterPooled>(obj)
    }

    @Override
    void destroyObject(PooledObject<FunTesterPooled> p, DestroyMode destroyMode) throws Exception {
        p.getObject().setName("") //回收资源
        super.destroyObject(p, destroyMode)
    }
}

对象池

static def initPool() {
    def config = new GenericObjectPoolConfig<FunTesterPooled>()
    config.setMaxIdle(10)
    config.setMinIdle(2)
    config.setMaxTotal(thread * 2)
    return new GenericObjectPool<FunTesterPooled>(new FunFactory(), config)
}

性能测试用例

static GenericObjectPool<FunTesterPooled> pool

static def desc = "池化框架性能测试"

static int times = 3000000

static int thread = 2

public static void main(String[] args) {
    this.pool = initPool()
    ThreadBase.COUNT = true
    RUNUP_TIME = 0
    def barrier = new CyclicBarrier(thread + 1)
    POOL_SIZE = thread
    def borrows = []
    thread.times {
        fun {
            borrows << pool.borrowObject()
            barrier.await()
        }
    }
    barrier.await()
    borrows.each {
        pool.returnObject(it)
    }
    output("对象创建完毕 创建数量${pool.getNumIdle()}")
    new Concurrent(new FunTester(), thread, desc).start()
    pool.close()
}

private static class FunTester extends FixedThread {


    FunTester() {
        super(null, times, true)
    }

    @Override
    protected void doing() throws Exception {
        pool.returnObject(pool.borrowObject())
    }

    @Override
    FunTester clone() {
        return new FunTester()
    }
}

其中往对象池中添加对象的时候,一开始我思路有点偏,所以想了一个java.util.concurrent.CyclicBarrier的方案。其实我们直接可以使用官方提供的org.apache.commons.pool2.ObjectPool#addObjects方法实现,代码非常简单,一行搞定pool.addObjects(thread)

测试结果

无等待

线程数 执行次数(万) QPS
1 300 1876172
2 300 1852364
5 300 1533912
10 300 1524538
10 100 1571623
20 100 1568692

可以看出,QPS 非常高,足够满足线性能测试需求。

等待

使用了休眠 2ms 的配置。

线程数 执行次数 (k) 单线程 QPS
20 10 410
50 10 406
100 5 406
200 2 404
300 2 403
400 2 403
500 2 262
800 2 143
1000 2 114

看来还是有瓶颈,在并发线程超过 400 多以后,这个平均单线程 QPS 会下降会多。猜测可能是org.apache.commons.pool2.impl.LinkedBlockingDequejava.util.concurrent.atomic.AtomicLong两个类的性能瓶颈。可以参考之前做过的Java&Go 高性能队列之 LinkedBlockingQueue 性能测试,虽然不一样可以参考。

虽然后面性能有所下降,瓶颈也在 10 万以上,也算是满足部分性能测试需求吧。后面我对com.funtest.PoolTest#GenericKeyedObjectPool对象池的性能,敬请期待。


↙↙↙阅读原文可查看相关链接,并与作者交流