cglib関連性能テストの比較


背景:
 
前の記事に続くcglibソース学習交流
多くの学生は、中国語のドキュメントが不足しているため、文章の紹介をよく理解していないが、具体的な使用を理解したいだけだと主張している.そこで勢いに乗ってこのブログを書きましたが、主にcglibのいくつかのツール類とよく使われるReflect、BeanUtilsを比較し、ついでにcglibの関連用法を紹介します.一挙両得、皆さんのご支持をお願いします.
 
本題:
1.まずPojo Beanを定義し、その後のテストは主にこれをめぐって行われる.
 
 
public static class CopyBean {

        private int        intValue;
        private boolean    boolValue;
        private float      floatValue;
        private double     doubleValue;
        private long       longValue;
        private char       charValue;
        private byte       byteValue;
        private short      shortValue;
        private Integer    integerValue;
        private Boolean    boolObjValue;
        private Float      floatObjValue;
        private Double     doubleObjValue;
        private Long       longObjValue;
        private Short      shortObjValue;
        private Byte       byteObjValue;
        private BigInteger bigIntegerValue;
        private BigDecimal bigDecimalValue;
        private String     stringValue;
......//    setter/getter  
}

 
説明:このcopyBeanにはjavaのすべてのプロトタイプオブジェクト、基本オブジェクト、および一般的なBigDecimal、BigIntegerの合計17のプロパティが含まれています.
 
2.テストテンプレートの定義(テンプレートモード)
 
TestCallbackインタフェースを定義します.
 
interface TestCallback {

    String getName();

    CglibPerformanceTest.CopyBean call(CglibPerformanceTest.CopyBean source);
}

 
 
テストのテンプレートメソッドの定義
   private static final DecimalFormat integerFormat = new DecimalFormat("#,###");
public static void testTemplate(TestCallback callback, CopyBean source, int count) {
        int warmup = 10;
        //      ,     ,      
        for (int i = 0; i < warmup; i++) {
            callback.call(source);
        }
        restoreJvm(); //   GC  
        //     
        long start = System.nanoTime();
        for (int i = 0; i < count; i++) {
            callback.call(source);
        }
        long nscost = (System.nanoTime() - start);
        System.out.println(callback.getName() + " total cost=" + integerFormat.format(nscost) + "ns , each cost="
                           + nscost / count  + "ns");
        restoreJvm();//   GC  

    }

 
 
説明:
 
  • より精確にテストするために、一度のサイクルで処理を行うため、jvmメモリ、GC、Classマウントがテストに与える影響を避けるために、warmupのプロセスがあり、まず少量のテスト方法を実行し、ここでは10回の
  • を実行する.
  • jvmメモリGCがテストidに与える影響を回避し、ここでrestoreJvmがjvm GC
  • を強制的に行う
    restoreJvm関連メソッド:
       
    private static void restoreJvm() {
            int maxRestoreJvmLoops = 10;
            long memUsedPrev = memoryUsed();
            for (int i = 0; i < maxRestoreJvmLoops; i++) {
                System.runFinalization();
                System.gc();
    
                long memUsedNow = memoryUsed();
                //      GC      ,    
                if ((ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount() == 0)
                    && (memUsedNow >= memUsedPrev)) {
                    break;
                } else {
                    memUsedPrev = memUsedNow;
                }
            }
        }
    
        private static long memoryUsed() {
            Runtime rt = Runtime.getRuntime();
            return rt.totalMemory() - rt.freeMemory();
        }
    

     
    3.元のCopyBeanデータの準備
     
    private static CopyBean getBean() {
            CopyBean bean = new CopyBean();
            bean.setIntValue(1);
            bean.setBoolValue(false);
            bean.setFloatValue(1.0f);
            bean.setDoubleValue(1.0d);
            bean.setLongValue(1l);
            bean.setCharValue('a');
            bean.setShortValue((short) 1);
            bean.setByteValue((byte) 1);
            bean.setIntegerValue(new Integer("1"));
            bean.setBoolObjValue(new Boolean("false"));
            bean.setFloatObjValue(new Float("1.0"));
            bean.setDoubleObjValue(new Double("1.0"));
            bean.setLongObjValue(new Long("1"));
            bean.setShortObjValue(new Short("1"));
            bean.setByteObjValue(new Byte("1"));
            bean.setBigIntegerValue(new BigInteger("1"));
            bean.setBigDecimalValue(new BigDecimal("1"));
            bean.setStringValue("1");
            return bean;
        }

     
     
    4.関連テストの実行
    テスト環境の説明:
     
  • オペレーティングシステムLinux ccbu-156-49 2.6.18-131.el5.customxen #1 SMP Tue Sep 15 15:46:11 CST 2009 x86_64 x86_64 x86_64 GNU/Linux
  • 仮想8 cpu、5 Gメモリ
  • jdk  1.6.0_18
  • jvmパラメータ
  • -server -Xmx2g -Xms2g -Xmn512m -XX:PermSize=196m -Xss256k -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70
     

  • 最初のテストは主に1つのオブジェクトのすべてのプロパティをコピーします.
  • BeanCopier  (cglib)
  • PropertyUtils (apache-common)
  • BeanUtils (apache-common)

  • 1.  BeanCopier  (cglib)
     
  • // beanCopier  
         final BeanCopier beanCopier = BeanCopier.create(CopyBean.class, CopyBean.class, false);
            final CopyBean beanCopierTarget = new CopyBean();//new  ,  new             
            testTemplate(new TestCallback() {
    
                public String getName() {
                    return "BeanCopier";
                }
    
                public CopyBean call(CopyBean source) {
                    beanCopier.copy(source, beanCopierTarget, null);
                    return beanCopierTarget;
                }
            }, bean, testCount);

  • 2. 
    PropertyUtils (apache-common)
  • // PropertyUtils  
            final CopyBean propertyUtilsTarget = new CopyBean();
            testTemplate(new TestCallback() {
    
                public String getName() {
                    return "PropertyUtils";
                }
    
                public CopyBean call(CopyBean source) {
                    try {
                        PropertyUtils.copyProperties(propertyUtilsTarget, source);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return propertyUtilsTarget;
                }
    
            }, bean, testCount);

  • 3. BeanUtils (apache-common)
  • // BeanUtils  
            final CopyBean beanUtilsTarget = new CopyBean();
            testTemplate(new TestCallback() {
    
                public String getName() {
                    return "BeanUtils";
                }
    
                public CopyBean call(CopyBean source) {
                    try {
                        BeanUtils.copyProperties(beanUtilsTarget, source);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return beanUtilsTarget;
                }
    
            }, bean, testCount);

  • テスト結果:
     
    テスト回数:testCount=1000*1000=100万回
  • BeanCopier total cost=36,626,000ns , each cost=36ns
  • PropertyUtils total cost=18,173,767,000ns , each cost=18173ns
  • BeanUtils total cost=31,236,079,000ns , each cost=31236ns

  • この結果から,BeanCopierはPropertyUtilsの504倍,PropertyUtilsはBeanUtilsの1.71倍,BeanCopierはPropertyUtilsの861.84倍と3桁近い差が見られた.
    第2のテストは主に1つのオブジェクトの単一のプロパティをコピーします.
  • BulkBean (cglib)
  • BeanMap (cglib)
  • FastClass/FastMethod  (cglib)
  • 未処理jdk reflect(jdk)
  • 処理jdk reflect(jdk)
  • 1. BulkBean 
    //   BulkBean
            final BulkBean bulkBean = BulkBean.create(bean.getClass(), new String[] { getMethodName },
                                                      new String[] { setMethodName }, new Class[] { Integer.class });
            final CopyBean bulkBeanTarget = new CopyBean();
            testTemplate(new TestCallback() {
    
                @Override
                public String getName() {
                    return "BulkBean";
                }
    
                @Override
                public CopyBean call(CopyBean source) {
                    Object[] result = bulkBean.getPropertyValues(source); //    getter
                    bulkBean.setPropertyValues(bulkBeanTarget, result); //    setter
                    return bulkBeanTarget;
                }
    
            }, bean, testCount);

     
    2. BeanMap
    //   BeanMap
            final BeanMap sourceMap = BeanMap.create(bean); //       
            final BeanMap targetMap = BeanMap.create(new CopyBean());
            final CopyBean beanMapTarget = new CopyBean();
            testTemplate(new TestCallback() {
    
                @Override
                public String getName() {
                    return "BeanMap";
                }
    
                @Override
                public CopyBean call(CopyBean source) {
                    targetMap.setBean(beanMapTarget); //         beanMap
                    Object obj = sourceMap.get(fieldName);
                    targetMap.put(fieldName, obj);
                    return beanMapTarget;
                }
    
            }, bean, testCount);

     3. FastClass/FastMethod
    //   FastClass
            final FastClass fastClass = FastClass.create(bean.getClass());
            final FastMethod setFastMetod = fastClass.getMethod(setMethodName, new Class[] { Integer.class });
            final FastMethod getFastMetod = fastClass.getMethod(getMethodName, new Class[] {});
            final CopyBean fastClassTarget = new CopyBean();
            testTemplate(new TestCallback() {
    
                @Override
                public String getName() {
                    return "FastClass";
                }
    
                @Override
                public CopyBean call(CopyBean source) {
    
                    try {
                        Object field = getFastMetod.invoke(source, new Object[] {});//   get  
                        setFastMetod.invoke(fastClassTarget, new Object[] { field });//   set    
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
    
                    return fastClassTarget;
                }
    
            }, bean, testCount);

     
    4.未処理jdk reflect
    try {
                //   method  cache,         cache method  
                final Method getMethod = bean.getClass().getMethod(getMethodName, new Class[] {});
                final Method setMethod = bean.getClass().getMethod(setMethodName, new Class[] { Integer.class });
                //        Reflect
                final CopyBean reflect1Target = new CopyBean();
                testTemplate(new TestCallback() {
    
                    @Override
                    public String getName() {
                        return "     Reflect";
                    }
    
                    @Override
                    public CopyBean call(CopyBean source) {
                        try {
                            Object field = getMethod.invoke(source, new Object[] {});
                            setMethod.invoke(reflect1Target, new Object[] { field });
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return reflect1Target;
                    }
    
                }, bean, testCount);
    
            } catch (Exception e1) {
                e1.printStackTrace();
            }
    
        }

     
    5.処理済みjdk reflect
    try {
                //   method  cache,         cache method  
                final Method getMethod = bean.getClass().getMethod(getMethodName, new Class[] {});
                final Method setMethod = bean.getClass().getMethod(setMethodName, new Class[] { Integer.class });
                //       Reflect
                getMethod.setAccessible(true);//      access    
                setMethod.setAccessible(true);//      access    
                final CopyBean reflect2Target = new CopyBean();
                testTemplate(new TestCallback() {
    
                    @Override
                    public String getName() {
                        return "    Reflect";
                    }
    
                    @Override
                    public CopyBean call(CopyBean source) {
                        try {
                            Object field = getMethod.invoke(source, new Object[] {});
                            setMethod.invoke(reflect2Target, new Object[] { field });
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return reflect2Target;
                    }
    
                }, bean, testCount);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
     
     
     
    テスト結果:
     
    テスト回数:testCount=1000*1000*100=1億回
  • BulkBean total cost=2,125,759,000ns , each cost=21ns
  • BeanMap total cost=2,730,912,000ns , each cost=27ns
  • FastClass total cost=2,576,470,000ns , each cost=25ns
  • 未処理のReflect total cost=288275000 ns、each cost=28 ns
  • で処理したReflect total cost=279280000 ns、each cost=27 ns
  • テストの結果、性能の差は多くなく、差は大きくありません.これはjdkがreflect呼び出しの最適化を素晴らしいことを示しています.
    最後に
    テストデータはアーチ参照のみで、最後にテストコードに添付ファイルが表示されます.テスト方法に問題があれば、レンガを撮ってください.