MethodHandle性能测试

   JDK7以来新加入包java.lang.invoke,提供了一种新的动态确定目标方法的机制,称为MethodHandle.相对于java.lang.reflect内的Method,两者支持层面并不同。Reflection是java api层面的反射调用,而MethodHandle则是从jvm层面支持调用。Reflection是重量级的,而MethodHandle则是轻量级的。

   为了更清楚地了解其中区别,我编写了代码对其执行速度进行测试。分别在有预热和无预热的状态下,对Reflection机制在极多次调用下,jvm似乎有能力将其自动编译优化,速度比MethodHandle更快。而在调用较少的情况下,MethodHandle有其轻量级的优势。

   以下为测试代码:

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.math.BigDecimal;

public class GoodestStatic {
	
	public static final int WARM_UP = 500_000; //预热次数
	public static int TEST_TIME= Integer.MAX_VALUE; //执行次数
	public volatile static boolean NEED_WARM_UP = false; //是否预热	
	
	public static final int good(int num){
		return num*5+1;
	}
	
	public static void main(String[] args) throws Throwable{
		if(args.length > 0){
			NEED_WARM_UP = "true".equalsIgnoreCase(args[0]);
		}
		
		if(args.length > 1){
			TEST_TIME = Integer.parseInt(args[1]);
		}
		
		System.out.println("WARM_UP ="+NEED_WARM_UP+",TEST_TIME="+TEST_TIME);
		
		test0(); //直接方法调用
		test1(); //Method反射调用
		test2(); //MethodHandle的invoke()调用
		test3(); //MethodHandle的invokeExact调用
	}

	private static void test0() throws Exception {		
		int sum = 0;
		if(NEED_WARM_UP)
		for(int i=0; i<WARM_UP;i++){
			sum += good(i);
		}
		
		long start = System.nanoTime();
		for(int i=0; i<TEST_TIME;i++){
			sum += good(i);
		}
		
		long end = System.nanoTime();
		System.out.println("Result ="+sum+",Test0 cost "+(end -start)+"ns.");	
		BigDecimal bd = new BigDecimal(end-start);
		bd = bd.divide(new BigDecimal(TEST_TIME),20,BigDecimal.ROUND_HALF_UP);
		System.out.println("per Time cost "+bd+" ns");
	}
	
	private static void test1() throws Exception {	
		Method m = GoodestStatic.class.getDeclaredMethod("good", int.class);
		
		int sum = 0;
		if(NEED_WARM_UP)
		for(int i=0; i<WARM_UP;i++){
			sum += (int)m.invoke(null, i);	
		}
		
		long start = System.nanoTime();
		for(int i=0; i<TEST_TIME;i++){
			sum += (int)m.invoke(null, i);			
		}
		long end = System.nanoTime();
		System.out.println("Result ="+sum+",Test1 cost "+(end -start)+"ns.");	
		BigDecimal bd = new BigDecimal(end-start);
		bd = bd.divide(new BigDecimal(TEST_TIME),20,BigDecimal.ROUND_HALF_UP);
		System.out.println("per Time cost "+bd+" ns");
	}
	
	private static void test2() throws Throwable {		
		MethodType mt = MethodType.methodType(int.class, int.class);
		MethodHandle m = MethodHandles.lookup().findStatic(GoodestStatic.class, "good", mt);
		
		int sum = 0;
		if(NEED_WARM_UP)
		for(int i=0; i<WARM_UP;i++){
			sum += (int)m.invoke(i);	
		}
		
		long start = System.nanoTime();
		for(int i=0; i<TEST_TIME;i++){
			sum += (int)m.invoke(i);		
		}
		long end = System.nanoTime();
		System.out.println("Result ="+sum+",Test2 cost "+(end -start)+"ns.");	
		BigDecimal bd = new BigDecimal(end-start);
        bd = bd.divide(new BigDecimal(TEST_TIME),20,BigDecimal.ROUND_HALF_UP);
		System.out.println("per Time cost "+bd+" ns");
	}
	
	private static void test3() throws Throwable {		
		MethodType mt = MethodType.methodType(int.class, int.class);
		MethodHandle m = MethodHandles.lookup().findStatic(GoodestStatic.class, "good", mt);
		
		int sum = 0;
		if(NEED_WARM_UP)
		for(int i=0; i<WARM_UP;i++){
			sum += (int)m.invokeExact(i);	
		}
				
		long start = System.nanoTime();
		for(int i=0; i<TEST_TIME;i++){
			sum += (int)m.invokeExact(i);		
		}
		long end = System.nanoTime();
		System.out.println("Result ="+sum+",Test3 cost "+(end -start)+"ns.");	
		BigDecimal bd = new BigDecimal(end-start);
		bd = bd.divide(new BigDecimal(TEST_TIME),20,BigDecimal.ROUND_HALF_UP);
		System.out.println("per Time cost "+bd+" ns");
	}
	
}

测试环境:

CPU:  Intel [email protected]

内存:  DDR3200 8Gx2 

OS:win7 64bit


先使用jvm1.7进行测试:

D:\Program Files\Java\jdk1.7.0_40\bin>java -version
java version "1.7.0_40"
Java(TM) SE Runtime Environment (build 1.7.0_40-b43)
Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode)

D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic true 5000
WARM_UP =true,TEST_TIME=5000
Result =-2003482716,Test0 cost 1169ns.
per Time cost 0.23380000000000000000 ns
Result =-2003482716,Test1 cost 101112ns.
per Time cost 20.22240000000000000000 ns
Result =-2003482716,Test2 cost 392176ns.
per Time cost 78.43520000000000000000 ns
Result =-2003482716,Test3 cost 31269ns.
per Time cost 6.25380000000000000000 ns

D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic false 5000
WARM_UP =false,TEST_TIME=5000
Result =62492500,Test0 cost 98190ns.
per Time cost 19.63800000000000000000 ns
Result =62492500,Test1 cost 3388143ns.
per Time cost 677.62860000000000000000 ns
Result =62492500,Test2 cost 34774205ns.
per Time cost 6954.84100000000000000000 ns
Result =62492500,Test3 cost 644957ns.
per Time cost 128.99140000000000000000 ns

D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic true
WARM_UP =true,TEST_TIME=2147483647
Result =1155250260,Test0 cost 508026604ns.
per Time cost 0.23656832251537978766 ns
Result =1155250260,Test1 cost 20721040125ns.
per Time cost 9.64898622345597773020 ns
Result =1155250260,Test2 cost 154064924123ns.
per Time cost 71.74207092949285680870 ns
Result =1155250260,Test3 cost 10129340931ns.
per Time cost 4.71684193970488474691 ns

D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic false
WARM_UP =false,TEST_TIME=2147483647
Result =-1073741820,Test0 cost 515627571ns.
per Time cost 0.24010779859503162959 ns
Result =-1073741820,Test1 cost 7117198964ns.
per Time cost 3.31420403314484471136 ns
Result =-1073741820,Test2 cost 164902029058ns.
per Time cost 76.78849116656393332713 ns
Result =-1073741820,Test3 cost 8496807506ns.
per Time cost 3.95663432309247288997 ns


D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic true 5000 
WARM_UP =true,TEST_TIME=5000
Result =-2003482716,Test0 cost 1169ns.
per Time cost 0.23380000000000000000 ns
Result =-2003482716,Test1 cost 120984ns.
per Time cost 24.19680000000000000000 ns
Result =-2003482716,Test2 cost 389546ns.
per Time cost 77.90920000000000000000 ns
Result =-2003482716,Test3 cost 34191ns.
per Time cost 6.83820000000000000000 ns

D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic false 5000 
WARM_UP =false,TEST_TIME=5000
Result =62492500,Test0 cost 96144ns.
per Time cost 19.22880000000000000000 ns
Result =62492500,Test1 cost 3296090ns.
per Time cost 659.21800000000000000000 ns
Result =62492500,Test2 cost 35162582ns.
per Time cost 7032.51640000000000000000 ns
Result =62492500,Test3 cost 609304ns.
per Time cost 121.86080000000000000000 ns

D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic true  
WARM_UP =true,TEST_TIME=2147483647
Result =1155250260,Test0 cost 508109013ns.
per Time cost 0.23660669719642340075 ns
Result =1155250260,Test1 cost 20455020598ns.
per Time cost 9.52511122800647803955 ns
Result =1155250260,Test2 cost 163458260579ns.
per Time cost 76.11618407774538922950 ns
Result =1155250260,Test3 cost 10206268212ns.
per Time cost 4.75266399642110988331 ns

D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic false  
WARM_UP =false,TEST_TIME=2147483647
Result =-1073741820,Test0 cost 516316363ns.
per Time cost 0.24042854236458825989 ns
Result =-1073741820,Test1 cost 7059888957ns.
per Time cost 3.28751698149718203652 ns
Result =-1073741820,Test2 cost 171611770599ns.
per Time cost 79.91295805150315074786 ns
Result =-1073741820,Test3 cost 8514055068ns.
per Time cost 3.96466584502005290474 ns

调用次数在测试中选用 小测试次数5000 和 大测试次数2147483647。从两次测试中可以看到,小次数调用的情况下,MethodHandle的invokeExact()方法确实具有优势。而在大次数调用下,Method的invokes执行速度变现怪异,在有预热的状况下执行速度反而处于劣势,推测因循环次数不用触发jit重编译引起,未能使用debug版测试环境验证推测。在没有预热的情况下,Method的invoke执行速度喜人,快于MethodHandle的invokeExact(),而MethodHandle的invoke()方法在jvm1.7中表现得非常差,无论小次数调用抑或大次数调用,执行速度都无法与其他两者相比。


再使用jvm1.8进行测试:

D:\Program Files\Java\jdk1.8.0_25\bin>java -version
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b18)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic true 5000
WARM_UP =true,TEST_TIME=5000
Result =-2003482716,Test0 cost 12566ns.
per Time cost 2.51320000000000000000 ns
Result =-2003482716,Test1 cost 98775ns.
per Time cost 19.75500000000000000000 ns
Result =-2003482716,Test2 cost 31561ns.
per Time cost 6.31220000000000000000 ns
Result =-2003482716,Test3 cost 53186ns.
per Time cost 10.63720000000000000000 ns

D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic false 5000
WARM_UP =false,TEST_TIME=5000
Result =62492500,Test0 cost 100528ns.
per Time cost 20.10560000000000000000 ns
Result =62492500,Test1 cost 2374097ns.
per Time cost 474.81940000000000000000 ns
Result =62492500,Test2 cost 1252508ns.
per Time cost 250.50160000000000000000 ns
Result =62492500,Test3 cost 432796ns.
per Time cost 86.55920000000000000000 ns

D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic true
WARM_UP =true,TEST_TIME=2147483647
Result =1155250260,Test0 cost 513498658ns.
per Time cost 0.23911644622642381407 ns
Result =1155250260,Test1 cost 20213309554ns.
per Time cost 9.41255575204852770644 ns
Result =1155250260,Test2 cost 10503014524ns.
per Time cost 4.89084726613520051638 ns
Result =1155250260,Test3 cost 8798333281ns.
per Time cost 4.09704320370081961327 ns

D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic false
WARM_UP =false,TEST_TIME=2147483647
Result =-1073741820,Test0 cost 513264286ns.
per Time cost 0.23900730825914410328 ns
Result =-1073741820,Test1 cost 6697535282ns.
per Time cost 3.11878290265741893214 ns
Result =-1073741820,Test2 cost 8808774164ns.
per Time cost 4.10190511872149310947 ns
Result =-1073741820,Test3 cost 8799512440ns.
per Time cost 4.09759229239895580914 ns


D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic true 5000 
WARM_UP =true,TEST_TIME=5000
Result =-2003482716,Test0 cost 12273ns.
per Time cost 2.45460000000000000000 ns
Result =-2003482716,Test1 cost 100528ns.
per Time cost 20.10560000000000000000 ns
Result =-2003482716,Test2 cost 33315ns.
per Time cost 6.66300000000000000000 ns
Result =-2003482716,Test3 cost 53479ns.
per Time cost 10.69580000000000000000 ns

D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic false 5000 
WARM_UP =false,TEST_TIME=5000
Result =62492500,Test0 cost 109003ns.
per Time cost 21.80060000000000000000 ns
Result =62492500,Test1 cost 2249897ns.
per Time cost 449.97940000000000000000 ns
Result =62492500,Test2 cost 1214518ns.
per Time cost 242.90360000000000000000 ns
Result =62492500,Test3 cost 413802ns.
per Time cost 82.76040000000000000000 ns

D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic true  
WARM_UP =true,TEST_TIME=2147483647
Result =1155250260,Test0 cost 515921264ns.
per Time cost 0.24024456005554858598 ns
Result =1155250260,Test1 cost 20449255728ns.
per Time cost 9.52242675121986621582 ns
Result =1155250260,Test2 cost 10516892353ns.
per Time cost 4.89730963385538646665 ns
Result =1155250260,Test3 cost 10538898336ns.
per Time cost 4.90755696823241047945 ns

D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic false  
WARM_UP =false,TEST_TIME=2147483647
Result =-1073741820,Test0 cost 516242136ns.
per Time cost 0.24039397772419917291 ns
Result =-1073741820,Test1 cost 7451620181ns.
per Time cost 3.46993104762860156951 ns
Result =-1073741820,Test2 cost 8833791018ns.
per Time cost 4.11355449916494754104 ns
Result =-1073741820,Test3 cost 8817718231ns.
per Time cost 4.10607002447641921438 ns

从1.8的测试中可以看出,无论是Reflection还是MethodHandle,两者的速度都有了提升 Method.invoke()与MethodHandle.invokeExact()的执行速度依然表现出与1.7环境下的差距。而MethodHandle的invoke()在1.8中得到了极大提升,速度逼近MethodHandle.invokeExact()。

   

        从测试中可以看出,只有在小次数调用下MethodHandle才会有优势。但小次数调用的代码通常不注重什么效率,这就使人觉得其地位尴尬了。可见,Reflection的Method反射机制仍然是我们的反射首选。



附CentOS下测试,i3-4160+8G ram:

[seamas@localhost ~]$ java -version
java version "1.7.0_75"
OpenJDK Runtime Environment (rhel-2.5.4.2.el7_0-x86_64 u75-b13)
OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode)
[seamas@localhost ~]$ java GoodestStatic true 5000
WARM_UP =true,TEST_TIME=5000
Result =-2003482716,Test0 cost 2571ns.
per Time cost 0.51420000000000000000 ns
Result =-2003482716,Test1 cost 97881ns.
per Time cost 19.57620000000000000000 ns
Result =-2003482716,Test2 cost 556485ns.
per Time cost 111.29700000000000000000 ns
Result =-2003482716,Test3 cost 51370ns.
per Time cost 10.27400000000000000000 ns
[seamas@localhost ~]$ java GoodestStatic false  5000
WARM_UP =false,TEST_TIME=5000
Result =62492500,Test0 cost 677493ns.
per Time cost 135.49860000000000000000 ns
Result =62492500,Test1 cost 10841897ns.
per Time cost 2168.37940000000000000000 ns
Result =62492500,Test2 cost 60150515ns.
per Time cost 12030.10300000000000000000 ns
Result =62492500,Test3 cost 2022143ns.
per Time cost 404.42860000000000000000 ns
[seamas@localhost ~]$ java GoodestStatic true
WARM_UP =true,TEST_TIME=2147483647
Result =1155250260,Test0 cost 648155339ns.
per Time cost 0.30182084967467042137 ns
Result =1155250260,Test1 cost 29811490738ns.
per Time cost 13.88205716008416244764 ns
Result =1155250260,Test2 cost 227465171362ns.
per Time cost 105.92172456342807252120 ns
Result =1155250260,Test3 cost 14363211021ns.
per Time cost 6.68839133702609284642 ns
[seamas@localhost ~]$ java GoodestStatic false
WARM_UP =false,TEST_TIME=2147483647
Result =-1073741820,Test0 cost 648307322ns.
per Time cost 0.30189162227413226956 ns
Result =-1073741820,Test1 cost 10100713630ns.
per Time cost 4.70351131386287105915 ns
Result =-1073741820,Test2 cost 232768978072ns.
per Time cost 108.39150202478817758373 ns
Result =-1073741820,Test3 cost 12097328362ns.
per Time cost 5.63325750065653468513 ns









你可能感兴趣的:(java,MathodHandle)