【问题标题】:JMH benchmarking avoid jvm optimizationJMH 基准测试避免 jvm 优化
【发布时间】:2021-04-07 15:37:49
【问题描述】:

我正在尝试编写 jmh 基准测试。

我看到各种博客提到 jmh 基准测试中的陷阱。常见的例子是

  1. 此代码
int sum() {
   int a =7;
   int b = 8;
   return a+b;
}

将优化为

int sum() {
return 15;
}
  1. 此代码
int sum(int y) {
   int x = new Object();
   return y;
}

将优化为

int sum(int y) {
   return y;
}

即删除未使用的对象初始化。

但是这个列表并没有广泛到 jvm 将做的所有类型的优化。

以下是我面临的问题。

假设方法很少,这是调用图的样子

int methodA(CustomObjectA a) {
   //do something 
   methodB(a);
   //do something
   return returnValueA;
}

int methodB(CustomObjectA a) {
   //do something 
   methodC(a);
   //do something
   return returnValueB;
}

int methodC(CustomObjectA a) {
   //do something
   return returnValueC;
}

我们将尝试对方法A 进行基准测试。通过传递在状态对象中创建的 CustomObjectA。但是

  1. 从 JVM 的角度来看,methodC 总是以相同的引用被调用,难道不会优化 methodC 以一直返回相同的 returnValueC 吗?

  2. 为什么不这样做?

  3. 我们如何确保不会进行这种优化?通过每次使用 @State(Scope.Thread) 传递不同的引用?

  4. 是否有详尽的列表来解释所有可能的优化?

【问题讨论】:

  • int methodC(CustomObjectA a) 中,如果您没有对它做任何事情,为什么还要将a 作为参数传递?
  • 我正在做某事

标签: java performance performance-testing microbenchmark jmh


【解决方案1】:

你是说你想测试methodA,而所有其他方法都是private,这就是调用链的样子吗?如果是这样,这里的JMH 无关紧要——将应用哪些优化,仍将应用于该代码。也很难说最终会发生什么优化,因为它们在JVM很多,并且还取决于许多其他因素,如操作系统、CPU 等;所以一个“广泛的列表”根本不存在。

例如,根据您在每个方法中的 //do something 中所做的操作,可以省略或不省略该代码。看看这个简化的例子:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 5)
@Measurement(iterations = 5, time = 5)
public class Sample {

    private static final int ME = 1;

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
                .include(Sample.class.getSimpleName())
                .build();

        new Runner(opt).run();
    }

    @Benchmark
    public int methodOne(CustomObjectA a) {
        simulateWork();
        return 42;
    }

    @Benchmark
    public int methodTwo(CustomObjectA a, Blackhole bh) {
        bh.consume(simulateWork());
        return 42;
    }

    @State(Scope.Thread)
    public static class CustomObjectA {

    }

    private static double simulateWork() {
        return ME << 1;
    }

}

不同之处在于,在方法@​​987654328@ 中,我使用了所谓的Blackhole(请阅读this 了解更多详细信息),而在methodOne 中,我没有。结果simulateWorkmethodOne中被消除,结果显示:

Benchmark         Mode  Cnt  Score   Error  Units
Sample.methodOne  avgt   25  1.950 ± 0.078  ns/op
Sample.methodTwo  avgt   25  3.955 ± 0.120  ns/op

另一方面,如果我稍微更改代码以使副作用最小:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 5)
@Measurement(iterations = 5, time = 5)
public class Sample {

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
                .include(Sample.class.getSimpleName())
                .build();

        new Runner(opt).run();
    }

    @Benchmark
    public int methodOne(CustomObjectA a) {
        simulateWorkWithA(a);
        return 42;
    }

    @Benchmark
    public int methodTwo(CustomObjectA a) {
        return simulateWorkWithA(a) + 42;
    }

    @Benchmark
    public int methodThree(CustomObjectA a, Blackhole bh) {
        bh.consume(simulateWorkWithA(a));
        return 42;
    }

    @State(Scope.Thread)
    public static class CustomObjectA {
        int x = 0;
    }

    private static int simulateWorkWithA(CustomObjectA a) {
        return a.x = a.x + 1;
    }

}

simulateWorkWithA(a)methodOne 中的消除不会发生:

Benchmark           Mode  Cnt  Score   Error  Units
Sample.methodOne    avgt   25  2.267 ± 0.198  ns/op
Sample.methodThree  avgt   25  3.711 ± 0.131  ns/op
Sample.methodTwo    avgt   25  2.325 ± 0.008  ns/op

请注意methodOnemethodTwo 之间几乎没有区别。

【讨论】:

  • 感谢您提及extensive list simply can't exist.。在这种情况下,这是我的问题,1.假设有一个静态方法,我们一遍又一遍地将相同的变量传递给它,它是否不会缓存相同输入参数的响应值(PropertyUtils.getProperty(Object bean,String名称)非常具体)? 2.在嵌套方法调用的情况下,我不能修改源代码一路通过黑洞吧? 3. 基准测试是查看所有优化可能发生的唯一方法吗?因为在这种情况下,很难确定其他人的基准是正确的。
  • @bestwishes for (1),是的,它可能会对此进行优化。所以呢?对于 (2) - 是的,你不能。但是看看我提供的第二个例子并理解它:即使执行了优化,它们仍然会以不可感知的方式发生。对于 (3) :我不太了解您的查询。这里的总体要点是:是的,发生了很多优化,分配可能会被忽略,变量可能会被内联等等 - 没关系。如果你想测量methodA - 按原样测量。
  • yes, it might optimise to that. so what? >>> 但是在产品上发生这种情况的可能性会更低吗?因此,我将报告的任何基准都是错误的。 it does not matter. If you want to measure methodA - measure it the way it is. >> 但在生产环境中,基准测试不会成立,因为那里的事情会更加随机(而不是固定的输入集。)并且在基准测试中执行的任何优化都不会在生产环境中完成。
  • @best希望它不会是“错误的”,它仍然是正确的,但与您在制作中看到的不符。请注意JMH 有不同的模式,例如仅在C1 jit 编译器中,或者也可以冷启动
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多