通用池化框架GenericObjectPool性能測試

之前寫過了- 通用池化框架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對象池的性能,敬請期待。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章