首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JMH基准测试避免jvm优化

JMH基准测试避免jvm优化
EN

Stack Overflow用户
提问于 2021-04-07 15:37:49
回答 1查看 221关注 0票数 2

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

我遇到了许多博客,提到了jmh基准测试中的缺陷。常见的例子是

  1. 这段代码
代码语言:javascript
复制
int sum() {
   int a =7;
   int b = 8;
   return a+b;
}

将被优化为

代码语言:javascript
复制
int sum() {
return 15;
}
  1. 这段代码
代码语言:javascript
复制
int sum(int y) {
   int x = new Object();
   return y;
}

将被优化为

代码语言:javascript
复制
int sum(int y) {
   return y;
}

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

但是,这个列表并没有扩展到各种优化jvm将做什么。

下面是我面临的问题。

让我们说有几种方法,下面是调用图的样子

代码语言:javascript
复制
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;
}

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

  1. 从JVM的角度来看,methodC总是以相同的引用被调用,它不是一直在优化methodC以返回相同的returnValueC吗?
  2. 它为什么不这么做?
  3. 我们如何才能确保不进行这种优化?每次使用@State(Scope.Thread)传递不同的引用?
  4. 是否有任何详尽的清单来解释什么是可能的优化?
EN

回答 1

Stack Overflow用户

发布于 2021-04-07 20:13:02

您是说要测试methodA,所有其他方法都是private,这就是调用链的样子吗?如果是这样的话,这里的JMH是无关紧要的--将应用什么优化,仍然将应用于该代码。也不可能知道最终会发生什么优化,因为它们在JVM上有很多,而且还依赖于许多其他因素,如操作系统、CPU等;因此,“广泛的列表”根本不可能存在。

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

代码语言:javascript
复制
@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;
    }

}

不同之处在于,在方法methodTwo中,我使用了一个所谓的Blackhole (阅读获取更多细节),而在methodOne中,我不使用。因此,从simulateWork中删除了methodOne,结果显示:

代码语言:javascript
复制
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

另一方面,如果我稍微修改代码,使其具有尽可能小的副作用:

代码语言:javascript
复制
@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中的消除将不会发生:

代码语言:javascript
复制
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几乎没有区别。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66989430

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档