文章目录
JMH分析if与switch性能差异
解决问题的本质就是回归源码和阅读说明文档
背景说明
我们知道switch-case结构,当case值是“密集”时,底层指令会优化为tableswitch结构,执行效率上要比“稀疏”时的lookupswitch好。
Java 虚拟机规范 基于JDK8->3.10 编译switch章节
编译器会使用tableswitch和lookupswitch指令来生成switch语句的编译代码。
tableswitch指令用于表示switch结构中的case语句块,它可以高效地从索引表中确定case语句块的分支偏移量。
当switch语句中的条件值不能对应于索引表中任何一个case语句块的分支偏移量时,default分支将起作用。
当switch语句中的case分支条件值比较稀疏时,tableswitch指令的空间使用率偏低。
该情况下可以使用lookupswitch指令来替代。lookupswitch指令的索引表项由int类型的键(来源于case语句块后面的数值)与对应的目标语句偏移量所构成。
当lookupswitch指令执行时,switch语句的条件值将和索引表中的键进行比较,如果某个键和条件值相符,那么将转移到这个键所对应的分支偏移量继续执行,如果没有键值符合,将执行default分支。
在从索引表确定分支偏移量的过程中,lookupswitch指令必须把条件值与不同的键进行比较,而tablesswitch指令则只需要对索引值进行一次范围检查。
因此,如果不太需要考虑空间效率,那么tableswitch指令会比lookupswitch指令有更高的执行效率。
-
我们将if条件判断语句进行反编译操作,看看底层是如何执行的
public static void main(String[] args) { int a = 4; if (1 == a) { System.out.println(1); } else if (2 == a) { System.out.println(2); } else if (3 == a) { System.out.println(3); } else { System.out.println(4); } }
- javap -c 字节码文件
public static void main(java.lang.String[]); Code: // 将int型4推送至栈顶 0: iconst_4 // 将栈顶int型数值存入第1个本地变量 1: istore_1 // 将int型1推送至栈顶 2: iconst_1 // 将第1个int型本地变量推送至栈顶 3: iload_1 // 比较栈顶两个int型数值大小,当结果不等于0时,就跳到第17个指令继续执行 4: if_icmpne 17 // 获取指定类的静态域,并将其值压入栈顶 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; // 将int型1推送至栈顶 10: iconst_1 // 调用实例方法 11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V // 无条件跳转 14: goto 54 17: iconst_2 18: iload_1 19: if_icmpne 32 22: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 25: iconst_2 26: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 29: goto 54 32: iconst_3 33: iload_1 34: if_icmpne 47 37: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 40: iconst_3 41: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 44: goto 54 47: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 50: iconst_4 51: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 54: return }
由上面的反编译内容,我们可以看到if条件判断语句实际上是逐个条件进行判断,也就说,可能出现不断比较直到最后一个条件语句才匹配的情况。
JMH 微基准测试 验证性能效率
JMH is a Java harness for building, running, and analysing nano/micro/milli/macro benchmarks written in Java and other languages targetting the JVM.
JMH is not intended to be used in the same way as a typical testing library such as JUnit. Simply adding the
jmh-core
jar file to your build is not enough to be able to run benchmarks
JMH,即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件。
主要是基于方法层面的基准测试,精度可达纳秒级别。
当希望进一步优化方法性能的时候,就可以使用JMH对优化结果进行量化分析。
引入依赖
<properties>
<jmh.version>1.25</jmh.version>
</properties>
<dependencies>
<!-- JMH基准测试框架依赖 -->
<!-- The jmh is a Java harness for building, running,
and analysing nano/micro/macro benchmarks written in Java and other languages targeting the JVM. -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<!-- JMH benchmark generator, based on annotation processors. -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>