jmh学习笔记-常量折叠

系列文章目录

jmh学习笔记-源代码编译与bench mode
jmh学习笔记-State共享对象
jmh学习笔记-State共享对象前后置方法
jmh学习笔记-代码清除
jmh学习笔记-常量折叠
jmh学习笔记-Forking分叉
jmh学习笔记-环境配置
jmh学习笔记-缓存行的处理方式
jmh学习笔记-自定义项目引入jmh



前言

消除死代码的另一面是常量折叠。 如果JVM意识到无论如何计算结果都是相同的,它可以巧妙地优化它。 这意味着我们可以将计算移到内部JMH循环之外,这样最后的测试结果是不精确的。 通过始终读取@State对象的非最终实例字段的输入,基于这些值计算结果并遵循规则以防止DCE,可以防止此情况。


提示:以下是本篇文章正文内容,下面案例可供参考

一、常量折叠

1、常量折叠

// IDEs will probably also say "Look, it could be final". Don't. Trust. Them. Either.
// (While this is normally fine advice, it does not work in the context of measuring correctly.)
private final double wrongX = Math.PI;

@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 Math.log(Math.PI);
}

@Benchmark
public double measureWrong_2() {
    // This is wrong: the source is predictable, and computation is foldable.
    return Math.log(wrongX);
}

在上面的基准测试代码当中,由于Math.PI与wrongX(通过final修饰)都是不可变的,所以measureWrong_1和measureWrong_2结果值都是不可变的,这样在迭代测试过程中针对这两个方法的循环都返回的是相同的值,JVM会判断出然后进行优化,不再真实通过Math.log计算结果,而是直接返回结果值。这就是所谓的常量折叠问题,属于JVM优化的一个方案。对应一般代码来说,这是优化,但是对于基准测试来说,则会使测试的结果不精准了.

2、避免常量折叠

jmh为了避免常量折叠,首先实例不能是final类型的,其次需要通过@State注解。

This can be prevented by always reading the inputs from non-final instance fields of @State objects, computing the result based on those values, and follow the rules to prevent DCE.

比如

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_10_ConstantFold {

    private double x = Math.PI;

    @Benchmark
    public double baseline() {
        // simply return the value, this is a baseline
        return Math.PI;
    }

    @Benchmark
    public double measureRight() {
        // This is correct: the source is not predictable.
        return Math.log(x);
    }
}

3、测试结果

针对以上代码进行测试如下

# JMH version: 1.26
# VM version: JDK 1.8.0_121, Java HotSpot(TM) 64-Bit Server VM, 25.121-b13
# VM invoker: C:\Program Files\Java\jdk1.8.0_121\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar=63626:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\bin -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.openjdk.jmh.samples.JMHSample_10_ConstantFold.baseline

# Run progress: 0.00% complete, ETA 00:06:40
# Fork: 1 of 1
# Warmup Iteration   1: 5.229 ns/op
# Warmup Iteration   2: 5.244 ns/op
# Warmup Iteration   3: 4.159 ns/op
# Warmup Iteration   4: 4.189 ns/op
# Warmup Iteration   5: 4.439 ns/op
Iteration   1: 4.321 ns/op
Iteration   2: 4.151 ns/op
Iteration   3: 4.157 ns/op
Iteration   4: 4.124 ns/op
Iteration   5: 4.135 ns/op


Result "org.openjdk.jmh.samples.JMHSample_10_ConstantFold.baseline":
  4.178 ±(99.9%) 0.313 ns/op [Average]
  (min, avg, max) = (4.124, 4.178, 4.321), stdev = 0.081
  CI (99.9%): [3.865, 4.490] (assumes normal distribution)


# JMH version: 1.26
# VM version: JDK 1.8.0_121, Java HotSpot(TM) 64-Bit Server VM, 25.121-b13
# VM invoker: C:\Program Files\Java\jdk1.8.0_121\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar=63626:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\bin -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.openjdk.jmh.samples.JMHSample_10_ConstantFold.measureRight

# Run progress: 25.00% complete, ETA 00:05:06
# Fork: 1 of 1
# Warmup Iteration   1: 35.830 ns/op
# Warmup Iteration   2: 35.950 ns/op
# Warmup Iteration   3: 35.603 ns/op
# Warmup Iteration   4: 35.940 ns/op
# Warmup Iteration   5: 35.849 ns/op
Iteration   1: 35.635 ns/op
Iteration   2: 29.650 ns/op
Iteration   3: 28.012 ns/op
Iteration   4: 27.998 ns/op
Iteration   5: 29.232 ns/op


Result "org.openjdk.jmh.samples.JMHSample_10_ConstantFold.measureRight":
  30.106 ±(99.9%) 12.234 ns/op [Average]
  (min, avg, max) = (27.998, 30.106, 35.635), stdev = 3.177
  CI (99.9%): [17.872, 42.339] (assumes normal distribution)


# JMH version: 1.26
# VM version: JDK 1.8.0_121, Java HotSpot(TM) 64-Bit Server VM, 25.121-b13
# VM invoker: C:\Program Files\Java\jdk1.8.0_121\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar=63626:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\bin -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.openjdk.jmh.samples.JMHSample_10_ConstantFold.measureWrong_1

# Run progress: 50.00% complete, ETA 00:03:23
# Fork: 1 of 1
# Warmup Iteration   1: 4.830 ns/op
# Warmup Iteration   2: 5.127 ns/op
# Warmup Iteration   3: 4.441 ns/op
# Warmup Iteration   4: 4.248 ns/op
# Warmup Iteration   5: 4.373 ns/op
Iteration   1: 4.125 ns/op
Iteration   2: 4.104 ns/op
Iteration   3: 4.115 ns/op
Iteration   4: 4.073 ns/op
Iteration   5: 4.192 ns/op


Result "org.openjdk.jmh.samples.JMHSample_10_ConstantFold.measureWrong_1":
  4.122 ±(99.9%) 0.170 ns/op [Average]
  (min, avg, max) = (4.073, 4.122, 4.192), stdev = 0.044
  CI (99.9%): [3.952, 4.292] (assumes normal distribution)


# JMH version: 1.26
# VM version: JDK 1.8.0_121, Java HotSpot(TM) 64-Bit Server VM, 25.121-b13
# VM invoker: C:\Program Files\Java\jdk1.8.0_121\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar=63626:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\bin -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.openjdk.jmh.samples.JMHSample_10_ConstantFold.measureWrong_2

# Run progress: 75.00% complete, ETA 00:01:41
# Fork: 1 of 1
# Warmup Iteration   1: 5.614 ns/op
# Warmup Iteration   2: 6.327 ns/op
# Warmup Iteration   3: 4.147 ns/op
# Warmup Iteration   4: 4.109 ns/op
# Warmup Iteration   5: 4.183 ns/op
Iteration   1: 4.164 ns/op
Iteration   2: 4.156 ns/op
Iteration   3: 4.188 ns/op
Iteration   4: 4.298 ns/op
Iteration   5: 4.148 ns/op


Result "org.openjdk.jmh.samples.JMHSample_10_ConstantFold.measureWrong_2":
  4.191 ±(99.9%) 0.238 ns/op [Average]
  (min, avg, max) = (4.148, 4.191, 4.298), stdev = 0.062
  CI (99.9%): [3.953, 4.429] (assumes normal distribution)


# Run complete. Total time: 00:06:45

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                 Mode  Cnt   Score    Error  Units
JMHSample_10_ConstantFold.baseline        avgt    5   4.178 ±  0.313  ns/op
JMHSample_10_ConstantFold.measureRight    avgt    5  30.106 ± 12.234  ns/op
JMHSample_10_ConstantFold.measureWrong_1  avgt    5   4.122 ±  0.170  ns/op
JMHSample_10_ConstantFold.measureWrong_2  avgt    5   4.191 ±  0.238  ns/op

从测试结果不难看出,常量折叠导致测试的平均值与真实计算相差很大。


总结

通常编译器的优化对程序执行有很大的帮助,但是在基准测试时必须要尽量避免它。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值