为什么 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
为什么 TestValueOutsideFinal
比 TestValueInside
快这么多?我们正在使用一个可能被认为是 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
消耗它。否则,您将无法获得预期的测量结果。
所以我做了以下基准测试来尝试理解 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
为什么 TestValueOutsideFinal
比 TestValueInside
快这么多?我们正在使用一个可能被认为是 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
消耗它。否则,您将无法获得预期的测量结果。