试图对lambda性能进行基准测试

And*_*niy 3 java performance lambda microbenchmark java-8

我读过这篇文章:Java 8 lambdas和匿名内部类之间的性能差异并提供了文章

它说:

Lambda调用的行为与匿名类调用完全相同

"好的"我说并决定编写我自己的基准测试,我已经使用过jmh,这里是下面的(我还添加了方法参考的基准).

public class MyBenchmark {

    public static final int TESTS_COUNT = 100_000_000;

    @Benchmark
    public void testMethod_lambda() {
        X x = i -> test(i);
        for (long i = 0; i < TESTS_COUNT; i++) {
            x.x(i);
        }
    }
    @Benchmark
    public void testMethod_methodRefernce() {
        X x = this::test;
        for (long i = 0; i < TESTS_COUNT; i++) {
            x.x(i);
        }
    }
    @Benchmark
    public void testMethod_anonymous() {
        X x = new X() {
            @Override
            public void x(Long i) {
                test(i);
            }
        };
        for (long i = 0; i < TESTS_COUNT; i++) {
            x.x(i);
        }
    }

    interface X {
        void x(Long i);
    }

    public void test(Long i) {
        if (i == null) System.out.println("never");
    }
}
Run Code Online (Sandbox Code Playgroud)

结果(英特尔酷睿i7 4770k)是:

Benchmark                                     Mode  Samples   Score  Score error  Units
t.j.MyBenchmark.testMethod_anonymous         thrpt      200  16,160        0,044  ops/s
t.j.MyBenchmark.testMethod_lambda            thrpt      200   4,102        0,029  ops/s
t.j.MyBenchmark.testMethod_methodRefernce    thrpt      200   4,149        0,022  ops/s
Run Code Online (Sandbox Code Playgroud)

因此,正如您所看到的,lambda和匿名方法调用之间有4倍的差异,其中lambda慢4倍.

问题是:我做错了什么或者我对lambdas的表现理论有误解?

编辑:

# VM invoker: C:\Program Files\Java\jre1.8.0_31\bin\java.exe
# VM options: <none>
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
Run Code Online (Sandbox Code Playgroud)

Tag*_*eev 9

问题出在您的基准测试中:您是死代码消除的受害者.

JIT编译器非常聪明,有时候理解自动装箱的结果永远不会为空,所以对于匿名类,它只是删除了你的检查,这反过来使循环体几乎为空.用不太明显的东西(对于JIT)替换它,如下所示:

public void test(Long i) {
    if (i == Long.MAX_VALUE) System.out.println("never");
}
Run Code Online (Sandbox Code Playgroud)

并且您将观察到相同的性能(匿名类变慢,而lambda和方法引用在同一级别执行).

对于lambda /方法引用,由于某种原因,它没有进行相同的优化.但你不应该担心:你不太可能在实际代码中有这样的方法,可以完全优化.

一般来说@apangin是对的:改用Blackhole.

  • 是的,我刚刚测试过,这确实是问题所在.一般来说@Andremoniy,你不应该试图在你的基准测试中聪明,JIT或者编译器会变得更聪明. (3认同)

Bri*_*etz 5

除了@TagirValeev提出的问题之外,您正在采取的基准方法存在根本缺陷,因为您正在测量复合指标(尽管您尝试不这样做.)

您想要独立衡量的重要成本是链接,捕获调用.但是你的所有测试都会将一些测试涂抹在一起,从而中毒你的结果.我的建议是只关注调用成本 - 这与整体应用程序吞吐量最相关,也是最容易测量的(因为它受到多级缓存的影响较小).

底线:在动态编译的环境中测量性能确实非常困难.即使是JMH.