我们借助 Oracle 官方提供的 JMH(JAVA 微基准测试套件)框架来进行测试.
代码如下
import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import java.util.concurrent.TimeUnit; @BenchmarkMode(Mode.AverageTime)//测试完成时间 @OutputTimeUnit(TimeUnit.SECONDS) @Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) // 预热 2 轮,每次 1s @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 3s @Fork(1)//fork 一个线程 @State(Scope.Thread)//每个线程一个实例 public class TestIfSwich { static int caseNum = 5; public static void main(String[] args) throws RunnerException { //建造者模式启动基准测试 Options options = new OptionsBuilder().include(TestIfSwich.class.getSimpleName()).output("C:\\Users\\Administrator\\IdeaProjects\\zzz\\src\\main\\resources\\ifswich2.log").build(); new Runner(options).run(); } @Benchmark public void switchTest(){ int num1; switch (caseNum){ case 1: num1 =1; break; case 2: num1 =2; break; case 3: num1 =3; break; case 4: num1 =4; break; case 5: num1 =5; break; } } @Benchmark public void ifTest(){ int num1; if(caseNum ==1){ num1=1; }else if(caseNum ==2){ num1=2; }else if(caseNum ==3){ num1=3; }else if(caseNum ==4){ num1=4; }else if(caseNum ==5) { num1 = 5; }else { num1=-1; } } }读取写出的日志查看跑出来的结果如下图所示
Benchmark Mode Cnt Score Error Units TestIfSwich.ifTest avgt 5 ≈ 10⁻⁸ s/op TestIfSwich.switchTest avgt 5 ≈ 10⁻⁹ s/opif的速度比switch慢了十倍,环境为windows本地,jdk11
注意:
Exception in thread "main" java.lang.RuntimeException: ERROR: Unable to find the resource: /META-INF/BenchmarkList at org.openjdk.jmh.runner.AbstractResourceReader.getReaders(AbstractResourceReader.java:98) at org.openjdk.jmh.runner.BenchmarkList.find(BenchmarkList.java:122) at org.openjdk.jmh.runner.Runner.internalRun(Runner.java:263) at org.openjdk.jmh.runner.Runner.run(Runner.java:209) at com.test.java8.LongAdderTest.main(LongAdderTest.java:27)开始在pom文件里只引入了如下导致上面报错
<dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.23</version> </dependency>通过再次添加如下依赖解决
<dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>1.23</version> <scope>provided</scope> </dependency>为了一探究竟为什么switch比if快,我们查看编译后的字节码通过命令javap -c D:\***.java
public class com.example.optimize.SwitchOptimize { static java.lang.Integer _NUM; public com.example.optimize.SwitchOptimize(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: invokestatic #7 // Method switchTest:()V 3: invokestatic #12 // Method ifTest:()V 6: return public static void switchTest(); Code: 0: getstatic #15 // Field _NUM:Ljava/lang/Integer; 3: invokevirtual #19 // Method java/lang/Integer.intValue:()I 6: tableswitch { // 1 to 9 1: 56 2: 83 3: 61 4: 83 5: 66 6: 83 7: 71 8: 83 9: 77 default: 83 } 56: iconst_1 57: istore_0 58: goto 85 61: iconst_3 62: istore_0 63: goto 85 66: iconst_5 67: istore_0 68: goto 85 71: bipush 7 73: istore_0 74: goto 85 77: bipush 9 79: istore_0 80: goto 85 83: iconst_m1 84: istore_0 85: return public static void ifTest(); Code: 0: getstatic #15 // Field _NUM:Ljava/lang/Integer; 3: invokevirtual #19 // Method java/lang/Integer.intValue:()I 6: iconst_1 7: if_icmpne 15 10: iconst_1 11: istore_0 12: goto 81 15: getstatic #15 // Field _NUM:Ljava/lang/Integer; 18: invokevirtual #19 // Method java/lang/Integer.intValue:()I 21: iconst_3 22: if_icmpne 30 25: iconst_3 26: istore_0 27: goto 81 30: getstatic #15 // Field _NUM:Ljava/lang/Integer; 33: invokevirtual #19 // Method java/lang/Integer.intValue:()I 36: iconst_5 37: if_icmpne 45 40: iconst_5 41: istore_0 42: goto 81 45: getstatic #15 // Field _NUM:Ljava/lang/Integer; 48: invokevirtual #19 // Method java/lang/Integer.intValue:()I 51: bipush 7 53: if_icmpne 62 56: bipush 7 58: istore_0 59: goto 81 62: getstatic #15 // Field _NUM:Ljava/lang/Integer; 65: invokevirtual #19 // Method java/lang/Integer.intValue:()I 68: bipush 9 70: if_icmpne 79 73: bipush 9 75: istore_0 76: goto 81 79: iconst_m1 80: istore_0 81: return static {}; Code: 0: iconst_1 1: invokestatic #25 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 4: putstatic #15 // Field _NUM:Ljava/lang/Integer; 7: return }发现if里面多次调用getstatic积少成多,if调用会被switch快。
还有就是switch解析过来是tableswitch,如果switch里面case的数字不是递增,而是乱序或者随机增加的,解析过来就是lookupswitch
tableswitch 和 lookupSwitchTest 当执行一次 tableswitch 时,堆栈顶部的 int 值直接用作表中的索引,以便抓取跳转目标并立即执行跳转。也就是说 tableswitch 的存储结构类似于数组,是直接用索引获取元素的,所以整个查询的时间复杂度是 O(1),这也意味着它的搜索速度非常快。
而执行 lookupswitch 时,会逐个进行分支比较或者使用二分法进行查询,因此查询时间复杂度是 O(log n),所以使用 lookupswitch 会比 tableswitch 慢