10.ConstantFold (常量折叠)
常数折叠是编译器最佳化技术,被使用在现代的编译器中。进阶的常数传播形式,或称之为稀疏有条件的常量传播,可以更精确地传播常数及无缝的移除无用的程式码。
eg: i=20+40+30; 出现了常量折叠。那么编译器会直接省略步骤,直接给我们结果,而不会去在CPU中进行计算的操作,这就是省去无用代码。
源码
package org.openjdk.jmh.samples; 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; @State(Scope.Thread) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //预热次数和时间 @Measurement(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //测试次数和时间 public class JMHSample_10_ConstantFold { private double x = Math.PI; //变量 private final double wrongX = Math.PI; //常量 private double compute(double d) { //计算方法 for (int c = 0; c < 10; c++) { d = d * d / Math.PI; } return d; } @Benchmark public double baseline() { // 获取常量 // simply return the value, this is a baseline return Math.PI; } @Benchmark public double measureWrong_1() { //计算常量 // This is wrong: the source is predictable, and computation is foldable. return compute(Math.PI); } @Benchmark public double measureWrong_2() { // 计算常量 // This is wrong: the source is predictable, and computation is foldable. return compute(wrongX); } @Benchmark public double measureRight() { //计算x x因为没有被final修饰,所以是变量 // This is correct: the source is not predictable. return compute(x); } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(JMHSample_10_ConstantFold.class.getSimpleName()) .forks(1) .build(); new Runner(opt).run(); } }
11.@OperationsPerInvocation (调用次数)
但报表只有一次
@OperationsPerInvocation(n) 告诉JMH的Benchmark一次执行相当于执行多少次,就是说JMH的Benchmark被JVM只 执行了一次,但在报表的时候会把这一次当作n次来计算。也就是输出这n次总耗时.
12.Loops (循环)
源码
package org.openjdk.jmh.samples; 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; /** * 计算两个数相加耗时: 理论上应该一致,但我们发现for循环更快 */ @State(Scope.Thread) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //预热次数和时间 @Measurement(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //测试次数和时间 public class JMHSample_11_Loops { int x = 1; int y = 2; @Benchmark public int measureRight() { //测试变量相加 return (x + y); } private int reps(int reps) { //进行循环相加 int s = 0; for (int i = 0; i < reps; i++) { s += (x + y); } return s; } @Benchmark @OperationsPerInvocation(1) //告诉JMH的Benchmark一次执行相当于执行多少次,就是说JMH的Benchmark被JVM只执行了一次,但在报表的时候会把这一次当作n次来计算。也就是输出这n次总耗时 public int measureWrong_1() { //循环相加 理论上应该与 第一个方式相加一样 return reps(1); } @Benchmark @OperationsPerInvocation(10) //告诉JMH一次执行相当于执行多少次,就是说JMH执行n次,只不过在报表的时候只会报出n/n个 public int measureWrong_10() { //循环相加 return reps(10); } @Benchmark @OperationsPerInvocation(100) public int measureWrong_100() { //循环相加 return reps(100); } @Benchmark @OperationsPerInvocation(1_000) public int measureWrong_1000() { //循环相加 return reps(1_000); } @Benchmark @OperationsPerInvocation(10_000) public int measureWrong_10000() { //循环相加 return reps(10_000); } @Benchmark @OperationsPerInvocation(100_000) public int measureWrong_100000() { //循环相加 return reps(100_000); } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(JMHSample_11_Loops.class.getSimpleName()) .forks(1) .build(); new Runner(opt).run(); } }
13.@fork (线程)
线程 fork(0)
package org.openjdk.jmh.samples; 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.lang.management.ManagementFactory; import java.util.concurrent.TimeUnit; /** * 我们发现进程pid是一样的,初步推测fork(0),代表没有进程新建 */ @State(Scope.Thread) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) //预热次数和时间 @Measurement(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) //测试次数和时间 public class JMHSample_12_Forking { @Benchmark @Fork(0) //通过源码我们可以直到,0代表 no fork public int measure_1_c1() { return 1; } @Setup(Level.Trial) // 在基准执行之前先打印pid。 public void setup() { printProcessID("setup"); } public static void printProcessID(String name) { //打印pid System.out.println(); System.out.println("--------------"); System.out.println(name + " pid is : " + ManagementFactory.getRuntimeMXBean().getName()); System.out.println("--------------"); System.out.println(); } public static void main(String[] args) throws RunnerException { printProcessID("main"); // 执行main方法的时候打印pid Options opt = new OptionsBuilder() .include(JMHSample_12_Forking.class.getSimpleName()) .build(); new Runner(opt).run(); //当他启动的时候,才会开始运行基准 } }
线程fork(1)
package org.openjdk.jmh.samples; 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.lang.management.ManagementFactory; import java.util.concurrent.TimeUnit; /** * 我们发现进程pid是一样的,初步推测fork(1),代表新开一个进程 */ @State(Scope.Thread) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) //预热次数和时间 @Measurement(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) //测试次数和时间 public class JMHSample_12_Forking { @Benchmark @Fork(1) //通过源码我们可以直到,1代表 1 fork public int measure_1_c1() { return 1; } @Setup(Level.Trial) // 在基准执行之前先打印pid。 public void setup() { printProcessID("setup"); } public static void printProcessID(String name) { //打印pid System.out.println(); System.out.println("--------------"); System.out.println(name + " pid is : " + ManagementFactory.getRuntimeMXBean().getName()); System.out.println("--------------"); System.out.println(); } public static void main(String[] args) throws RunnerException { printProcessID("main"); // 执行main方法的时候打印pid Options opt = new OptionsBuilder() .include(JMHSample_12_Forking.class.getSimpleName()) .build(); new Runner(opt).run(); //当他启动的时候,才会开始运行基准 } }