为什么 Java 使用包含 X 的外部变量的函数比直接使用 X 更快?

Why is Java faster with a function using an external variable containing X rather than using X directly?

所以我做了以下基准测试来尝试理解 Lambas 是如何影响性能的。

@Fork(1)
@Measurement(iterations = 5)
@Warmup(iterations = 5)
public class LambdaBenchmark {

    @State(Scope.Thread)
    public static class MyInteger {
        public Integer value = 10;
    }

    @Benchmark
    public void TestValueInside(MyInteger integer) {
        Function<Integer, Integer> toTest = i -> i + 10;
        toTest.apply(1);
    }

    @Benchmark
    public void TestValueOutside(MyInteger integer) {
        Function<Integer, Integer> toTest = i -> i + integer.value;
        toTest.apply(1);
    }

    @Benchmark
    public void TestValueOutsideFinal(MyInteger integer) {
        int i2 = 10;
        Function<Integer, Integer> toTest = i -> i + i2;
        toTest.apply(1);
    }

    @Benchmark
    public void TestValueOutsideLocalCopy(MyInteger integer) {
        int i2 = integer.value;
        Function<Integer, Integer> toTest = i -> i + i2;
        toTest.apply(1);
    }
}

我对结果有点困惑:

Benchmark                                   Mode  Cnt           Score           Error  Units
LambdaBenchmark.TestValueInside            thrpt    5  1494683335,686 ▒ 157769032,327  ops/s
LambdaBenchmark.TestValueOutside           thrpt    5   755197977,631 ▒  39587849,696  ops/s
LambdaBenchmark.TestValueOutsideFinal      thrpt    5  3007751583,191 ▒ 178696557,885  ops/s
LambdaBenchmark.TestValueOutsideLocalCopy  thrpt    5   771307179,267 ▒  13613431,113  ops/s

为什么 TestValueOutsideFinalTestValueInside 快这么多?我们正在使用一个可能被认为是 final 的外部变量,但它仍然是一个变量而不是直接值?还是值 10 不断被重新创建而不是总是使用相同的寻址变量?

编辑:

考虑到@AlBlue 所说的内容后,它确实显示出更接近的结果。

以下是我 return 每个值的结果:

Benchmark                                   Mode  Cnt          Score          Error  Units
LambdaBenchmark.TestValueInside            thrpt    5  309129197,389 ▒ 32089680,994  ops/s
LambdaBenchmark.TestValueOutside           thrpt    5  283435336,319 ▒ 52230809,938  ops/s
LambdaBenchmark.TestValueOutsideFinal      thrpt    5  360590518,854 ▒  3072257,599  ops/s
LambdaBenchmark.TestValueOutsideLocalCopy  thrpt    5  279159794,477 ▒ 12871790,409  ops/s

TestValueInside 和 TestValueOutsideLocalCopy 最快,因为每秒的操作数最高。

这可能是因为在 + 中使用了 int。使用可能为 null 的整数,需要拆箱为 int 值,因此引发 NullPointerException 需要额外检查。

您的代码陷入了基准测试中最古老的问题:您忽略了方法的结果。结果一切都被扔掉了,你测量的是随机数据。

总是,总是,总是return函数调用的值,或者用Blackhole.consume消耗它。否则,您将无法获得预期的测量结果。