测试 Lambda 表达式性能的正确方法?
Right way to test performance of Lambda expressions?
我已经使用 JMH 来测试 Lambda 对 Anonymous Inner 类 的性能,下面是我是如何做到的:
public class LambdaVsAnonymousClass {
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void testLambda() {
// nonCapturing expressions
NonCapturing nonCapturing = () -> System.out.println("ram");
nonCapturing.test();
// nonCapturing expressions
NonCapturing nonCapturing2 = () -> System.out.println("bon");
nonCapturing2.test();
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void testAnonymousClass() {
// anonymous classes
NonCapturing nonCapturing = new NonCapturing() {
@Override
public void test() {
System.out.println("ram");
}
};
nonCapturing.test();
// anonymous classes
NonCapturing nonCapturing2 = new NonCapturing() {
@Override
public void test() {
System.out.println("bon");
}
};
nonCapturing2.test();
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void testLambdaCapturing() {
// lambda expressions
final int i = 1;
Capturing capturing = n -> System.out.println(n);
capturing.test(i);
// lambda expressions
final int j = 2;
Capturing capturing2 = n -> System.out.println(n);
capturing2.test(j);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void testAnonymousClassCapturing() {
// anonymous classes
final int i = 1;
Capturing capturing = new Capturing() {
@Override
public void test(int n) {
System.out.println(n);
}
};
capturing.test(i);
// anonymous classes
final int j = 2;
Capturing capturing2 = new Capturing() {
@Override
public void test(int n) {
System.out.println(n);
}
};
capturing2.test(j);
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(LambdaVsAnonymousClass.class.getSimpleName())
.warmupIterations(5)
.measurementIterations(5)
.forks(1)
.build();
new Runner(opt).run();
}
}
@FunctionalInterface
interface NonCapturing {
void test();
}
@FunctionalInterface
interface Capturing {
void test(int n);
}
我原以为 Lambda 会更快完成,但我在下面得到了相反的结果。我是否错误地测试了它?如果是,实际测试 lambda 表达式的更快性能的正确方法是什么。
# Run complete. Total time: 00:00:42
Benchmark Mode Cnt Score Error Units
LambdaVsAnonymousClass.testAnonymousClass avgt 5 16.592 ± 4.084 us/op
LambdaVsAnonymousClass.testAnonymousClassCapturing avgt 5 18.916 ± 4.469 us/op
LambdaVsAnonymousClass.testLambda avgt 5 18.618 ± 4.060 us/op
LambdaVsAnonymousClass.testLambdaCapturing avgt 5 22.008 ± 16.729 us/op
NOTE: The concept of capturing lambda expressions is incorrectly shown in the code. Read comments from @Holger for more understanding.
不是很清楚你到底想测试什么。创建然后调用?如果是这样,然后删除 System.out 因为它们咬你很厉害然后输出应该在 nano-seconds.
中测量
这种情况下的结果(毫不奇怪)大致相同:
LambdaVsClass.testAnonymousClass avgt 5 0.335 ± 0.069 ns/op
LambdaVsClass.testAnonymousClassCapturing avgt 5 0.337 ± 0.051 ns/op
LambdaVsClass.testLambda avgt 5 0.331 ± 0.051 ns/op
LambdaVsClass.testLambdaCapturing avgt 5 0.337 ± 0.043 ns/op
如果您在 invokedynamic(没有任何预热)引导 lambda 时测量 SingleShotTime,结果应该不同。
您应该对基准测试结果持批判态度,并理解您所看到的。 ±4
的错误大于 16.6
和 18.6
.
的“非捕获”测试执行时间之间的差异
当您看到 22
错误 ±16.7
!
这样的结果时,它肯定会敲响警钟
另外,你对“捕获”的理解有误。在您的测试中,没有捕获 lambda 表达式或匿名 class。您只有一个参数为零的函数和一个参数为一个的函数。这与捕获无关。唯一的区别是,在一种情况下,您正在打印现有的 String
,而在另一种情况下,您正在执行 int
到 String
的转换。但即使是这些差异也小于报告的错误。
当考虑错误时,结果在相同的数量级内,当然,打印代码的性能不取决于它是驻留在 lambda 表达式还是匿名内部 class。目前尚不清楚为什么您希望 lambda 表达式更快。 Lambda 表达式并不神奇。
我已经使用 JMH 来测试 Lambda 对 Anonymous Inner 类 的性能,下面是我是如何做到的:
public class LambdaVsAnonymousClass {
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void testLambda() {
// nonCapturing expressions
NonCapturing nonCapturing = () -> System.out.println("ram");
nonCapturing.test();
// nonCapturing expressions
NonCapturing nonCapturing2 = () -> System.out.println("bon");
nonCapturing2.test();
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void testAnonymousClass() {
// anonymous classes
NonCapturing nonCapturing = new NonCapturing() {
@Override
public void test() {
System.out.println("ram");
}
};
nonCapturing.test();
// anonymous classes
NonCapturing nonCapturing2 = new NonCapturing() {
@Override
public void test() {
System.out.println("bon");
}
};
nonCapturing2.test();
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void testLambdaCapturing() {
// lambda expressions
final int i = 1;
Capturing capturing = n -> System.out.println(n);
capturing.test(i);
// lambda expressions
final int j = 2;
Capturing capturing2 = n -> System.out.println(n);
capturing2.test(j);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public void testAnonymousClassCapturing() {
// anonymous classes
final int i = 1;
Capturing capturing = new Capturing() {
@Override
public void test(int n) {
System.out.println(n);
}
};
capturing.test(i);
// anonymous classes
final int j = 2;
Capturing capturing2 = new Capturing() {
@Override
public void test(int n) {
System.out.println(n);
}
};
capturing2.test(j);
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(LambdaVsAnonymousClass.class.getSimpleName())
.warmupIterations(5)
.measurementIterations(5)
.forks(1)
.build();
new Runner(opt).run();
}
}
@FunctionalInterface
interface NonCapturing {
void test();
}
@FunctionalInterface
interface Capturing {
void test(int n);
}
我原以为 Lambda 会更快完成,但我在下面得到了相反的结果。我是否错误地测试了它?如果是,实际测试 lambda 表达式的更快性能的正确方法是什么。
# Run complete. Total time: 00:00:42
Benchmark Mode Cnt Score Error Units
LambdaVsAnonymousClass.testAnonymousClass avgt 5 16.592 ± 4.084 us/op
LambdaVsAnonymousClass.testAnonymousClassCapturing avgt 5 18.916 ± 4.469 us/op
LambdaVsAnonymousClass.testLambda avgt 5 18.618 ± 4.060 us/op
LambdaVsAnonymousClass.testLambdaCapturing avgt 5 22.008 ± 16.729 us/op
NOTE: The concept of capturing lambda expressions is incorrectly shown in the code. Read comments from @Holger for more understanding.
不是很清楚你到底想测试什么。创建然后调用?如果是这样,然后删除 System.out 因为它们咬你很厉害然后输出应该在 nano-seconds.
中测量这种情况下的结果(毫不奇怪)大致相同:
LambdaVsClass.testAnonymousClass avgt 5 0.335 ± 0.069 ns/op
LambdaVsClass.testAnonymousClassCapturing avgt 5 0.337 ± 0.051 ns/op
LambdaVsClass.testLambda avgt 5 0.331 ± 0.051 ns/op
LambdaVsClass.testLambdaCapturing avgt 5 0.337 ± 0.043 ns/op
如果您在 invokedynamic(没有任何预热)引导 lambda 时测量 SingleShotTime,结果应该不同。
您应该对基准测试结果持批判态度,并理解您所看到的。 ±4
的错误大于 16.6
和 18.6
.
的“非捕获”测试执行时间之间的差异
当您看到 22
错误 ±16.7
!
另外,你对“捕获”的理解有误。在您的测试中,没有捕获 lambda 表达式或匿名 class。您只有一个参数为零的函数和一个参数为一个的函数。这与捕获无关。唯一的区别是,在一种情况下,您正在打印现有的 String
,而在另一种情况下,您正在执行 int
到 String
的转换。但即使是这些差异也小于报告的错误。
当考虑错误时,结果在相同的数量级内,当然,打印代码的性能不取决于它是驻留在 lambda 表达式还是匿名内部 class。目前尚不清楚为什么您希望 lambda 表达式更快。 Lambda 表达式并不神奇。