Java 测试Lambda表达式性能的正确方法?
我曾经针对匿名内部类测试Lambda的性能,下面是我是如何做到这一点的:Java 测试Lambda表达式性能的正确方法?,java,lambda,java-8,anonymous-inner-class,Java,Lambda,Java 8,Anonymous Inner Class,我曾经针对匿名内部类测试Lambda的性能,下面是我是如何做到这一点的: public class LambdaVsAnonymousClass { @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MICROSECONDS) public void testLambda() { // nonCapturing expressions NonCapturing nonCapturing =
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
注意:捕获lambda表达式的概念在代码中显示不正确。阅读@Holger的评论,了解更多信息
你到底想测试什么还不是很清楚。创造然后调用?如果是这样的话,那么就把系统移除,因为它们咬得很厉害,输出应该以纳秒为单位
这种情况下的结果(毫不奇怪)大致相同:
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
如果在InvokedDynamic(无任何预热)引导lambda时测量SingleShotTime,则结果应该有所不同。您应该对基准测试结果持批评态度,并了解您所看到的内容。
±4
的错误大于您的“非捕获”测试执行时间16.6
和18.6
之间的差异当你看到像
22
这样的错误为±16.7
的结果时,它肯定会敲响警钟
另外,你对“捕获”有错误的理解。在测试中,没有捕获lambda表达式或匿名类。您只有一个参数为零的函数和一个参数为一的函数。这与捕获无关。唯一的区别是,在一种情况下,您正在打印现有的字符串
,而在另一种情况下,您正在执行int
到字符串
的转换。但即使这些差异也比报告的误差小
当考虑错误时,结果在相同的范围内,当然,打印代码的性能并不取决于它是驻留在lambda表达式中还是驻留在匿名内部类中。不清楚为什么您希望lambda表达式更快。Lambda表达式并不神奇。我会尝试在基准测试期间不打印到控制台(
System.out.println()
),因为控制台/操作系统会对性能产生巨大影响,因此可能会扭曲结果。请看一看。除非您是足够的性能专家,比如说,编写JMH,我的建议是:不要再担心nano性能问题,而是专注于编写清晰的代码。他们足够快了。@BrianGoetz我实际上是在为我大学的演讲做准备。但是非常感谢你的建议。我认为Lambdas速度更快,因为不需要创建单独的类文件和加载类等,因为Lambdas被转换为静态方法,然后还有lambda工厂的优化,我猜这是一条冷路。请纠正我。我不理解你的话,“没有捕捉lambda表达式”。我想我正在将final
变量传递给lambda,它位于lambda表达式之外。你能纠正我吗?你正在把一个值作为参数传递给接口方法。这个值也可以是一个常量,比如42
,或者是一个表达式,比如“bla”.length()
,或者是一个可变变量,这无关紧要。这与捕获无关。捕获发生在lambda表达式本身从外部上下文引用变量时,而不是在使用参数时。与加载类相比,lambda表达式的冷启动速度更快,但这仅适用于第一次执行,因为类只加载一次。在测量另外五次执行之前,您正在进行五次预热迭代。你希望如何见证第一次执行的不同之处?此外,当您对只包含两个lambda表达式的方法进行基准测试时,冷启动性能将因lambda元工厂的初始化而黯然失色,因为该类(以及内部使用的所有类)也必须加载。您的评论澄清了@holger的许多问题