Joh*_*röm 9 java performance lambda jvm java-stream
我在lambda vs方法参考上运行了一些JMH测试,看起来类似于:
IntStream......reduce(Integer::max)
vs.
IntSream.......reduce((i1, i2) -> Integer.max(i1, i2))
Run Code Online (Sandbox Code Playgroud)
我注意到的是,与Java中的lambda相比,方法引用的执行速度大约是lambda的5倍。当我在Java 11中运行测试时,两种方法的执行时间都与Java中方法引用的执行速度差不多。 8.因此,Java 11中的lambda和方法引用之间在性能上没有重大区别。
我的问题是:从Java 8到11进行了哪些改进以提高性能?我正在使用OpenJDK。
编辑我的基准:
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Fork(value = 1, jvmArgs = {"-XX:CompileThreshold=5000"})
@Warmup(iterations = 2)
public class FindMaxInt {
@Param({"10000", "1000000", "10000000"})
private int n;
private List<Integer> data;
@Setup
public void setup(){
data = createData();
}
@Benchmark
public void streamWithMethodReference(final Blackhole blackhole){
int max = data.stream().mapToInt(Integer::intValue).reduce(Integer.MIN_VALUE, Integer::max);
blackhole.consume(max);
}
@Benchmark
public void streamWithLambda(final Blackhole blackhole){
int max = data.stream().mapToInt(Integer::intValue).reduce(Integer.MIN_VALUE, (i1, i2) -> Integer.max(i1, i2));
blackhole.consume(max);
}
Run Code Online (Sandbox Code Playgroud)
apa*_*gin 18
不同的内联树解释了不同的结果。与方法引用相比,Lambda具有更高的间接级别,因此在JIT编译期间,带有lambda的表达式可能会更早达到内联深度限制。默认值为-XX:MaxInlineLevel=9。
运行基准测试-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining以查看整个内联树。这是我们在JDK 8上获得的东西:
1563 560 4 bench.FindMaxInt::streamWithLambda (38 bytes)
@ 3 java.util.stream.IntPipeline::<init> (7 bytes) inline (hot)
@ 3 java.util.stream.AbstractPipeline::<init> (91 bytes) inline (hot)
@ 1 java.util.stream.PipelineHelper::<init> (5 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 51 java.util.stream.StreamOpFlag::combineOpFlags (9 bytes) inline (hot)
@ 2 java.util.stream.StreamOpFlag::getMask (30 bytes) inline (hot)
@ 66 java.util.stream.IntPipeline$StatelessOp::opIsStateful (2 bytes) inline (hot)
@ 4 java.util.Collection::stream (11 bytes) inline (hot)
\-> TypeProfile (5120/5120 counts) = java/util/ArrayList
@ 1 java.util.ArrayList::spliterator (12 bytes) inline (hot)
@ 8 java.util.ArrayList$ArrayListSpliterator::<init> (26 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 7 java.util.stream.StreamSupport::stream (19 bytes) inline (hot)
@ 1 java.util.Objects::requireNonNull (14 bytes) inline (hot)
@ 11 java.util.stream.StreamOpFlag::fromCharacteristics (37 bytes) inline (hot)
@ 1 java.util.ArrayList$ArrayListSpliterator::characteristics (4 bytes) inline (hot)
\-> TypeProfile (5124/5124 counts) = java/util/ArrayList$ArrayListSpliterator
@ 15 java.util.stream.ReferencePipeline$Head::<init> (8 bytes) inline (hot)
@ 4 java.util.stream.ReferencePipeline::<init> (8 bytes) inline (hot)
@ 4 java.util.stream.AbstractPipeline::<init> (55 bytes) inline (hot)
@ 1 java.util.stream.PipelineHelper::<init> (5 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 9 java.lang.invoke.LambdaForm$MH/883049899::linkToTargetMethod (8 bytes) force inline by annotation
@ 4 java.lang.invoke.LambdaForm$MH/1922154895::identity_L (8 bytes) force inline by annotation
@ 14 java.util.stream.ReferencePipeline::mapToInt (26 bytes) inline (hot)
\-> TypeProfile (5120/5120 counts) = java/util/stream/ReferencePipeline$Head
@ 1 java.util.Objects::requireNonNull (14 bytes) inline (hot)
@ 22 java.util.stream.ReferencePipeline$4::<init> (20 bytes) inline (hot)
@ 16 java.util.stream.IntPipeline$StatelessOp::<init> (29 bytes) inline (hot)
@ 3 java.util.stream.IntPipeline::<init> (7 bytes) inline (hot)
@ 3 java.util.stream.AbstractPipeline::<init> (91 bytes) inline (hot)
@ 1 java.util.stream.PipelineHelper::<init> (5 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 51 java.util.stream.StreamOpFlag::combineOpFlags (9 bytes) inline (hot)
@ 2 java.util.stream.StreamOpFlag::getMask (30 bytes) inline (hot)
@ 66 java.util.stream.IntPipeline$StatelessOp::opIsStateful (2 bytes) inline (hot)
@ 21 java.lang.invoke.LambdaForm$MH/883049899::linkToTargetMethod (8 bytes) force inline by annotation
@ 4 java.lang.invoke.LambdaForm$MH/1922154895::identity_L (8 bytes) force inline by annotation
@ 26 java.util.stream.IntPipeline::reduce (16 bytes) inline (hot)
\-> TypeProfile (5120/5120 counts) = java/util/stream/ReferencePipeline$4
@ 3 java.util.stream.ReduceOps::makeInt (18 bytes) inline (hot)
@ 1 java.util.Objects::requireNonNull (14 bytes) inline (hot)
@ 14 java.util.stream.ReduceOps$5::<init> (16 bytes) inline (hot)
@ 12 java.util.stream.ReduceOps$ReduceOp::<init> (10 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 6 java.util.stream.AbstractPipeline::evaluate (94 bytes) inline (hot)
@ 50 java.util.stream.AbstractPipeline::isParallel (8 bytes) inline (hot)
@ 80 java.util.stream.TerminalOp::getOpFlags (2 bytes) inline (hot)
\-> TypeProfile (5130/5130 counts) = java/util/stream/ReduceOps$5
@ 85 java.util.stream.AbstractPipeline::sourceSpliterator (265 bytes) inline (hot)
@ 79 java.util.stream.AbstractPipeline::isParallel (8 bytes) inline (hot)
@ 88 java.util.stream.ReduceOps$ReduceOp::evaluateSequential (18 bytes) inline (hot)
@ 2 java.util.stream.ReduceOps$5::makeSink (5 bytes) inline (hot)
@ 1 java.util.stream.ReduceOps$5::makeSink (16 bytes) inline (hot)
@ 12 java.util.stream.ReduceOps$5ReducingSink::<init> (15 bytes) inline (hot)
@ 11 java.lang.Object::<init> (1 bytes) inline (hot)
@ 6 java.util.stream.AbstractPipeline::wrapAndCopyInto (18 bytes) inline (hot)
@ 3 java.util.Objects::requireNonNull (14 bytes) inline (hot)
@ 9 java.util.stream.AbstractPipeline::wrapSink (37 bytes) inline (hot)
@ 1 java.util.Objects::requireNonNull (14 bytes) inline (hot)
@ 23 java.util.stream.ReferencePipeline$4::opWrapSink (10 bytes) inline (hot)
\-> TypeProfile (5081/5081 counts) = java/util/stream/ReferencePipeline$4
@ 6 java.util.stream.ReferencePipeline$4$1::<init> (11 bytes) inline (hot)
@ 7 java.util.stream.Sink$ChainedReference::<init> (16 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 6 java.util.Objects::requireNonNull (14 bytes) inline (hot)
@ 13 java.util.stream.AbstractPipeline::copyInto (53 bytes) inline (hot)
@ 1 java.util.Objects::requireNonNull (14 bytes) inline (hot)
@ 9 java.util.stream.AbstractPipeline::getStreamAndOpFlags (5 bytes) accessor
@ 12 java.util.stream.StreamOpFlag::isKnown (19 bytes) inline (hot)
@ 20 java.util.Spliterator::getExactSizeIfKnown (25 bytes) inline (hot)
\-> TypeProfile (5081/5081 counts) = java/util/ArrayList$ArrayListSpliterator
@ 1 java.util.ArrayList$ArrayListSpliterator::characteristics (4 bytes) inline (hot)
@ 19 java.util.ArrayList$ArrayListSpliterator::estimateSize (11 bytes) inline (hot)
@ 1 java.util.ArrayList$ArrayListSpliterator::getFence (48 bytes) inline (hot)
@ 38 java.util.ArrayList::access$000 (5 bytes) accessor
@ 25 java.util.stream.Sink$ChainedReference::begin (11 bytes) inline (hot)
\-> TypeProfile (5081/5081 counts) = java/util/stream/ReferencePipeline$4$1
@ 5 java.util.stream.ReduceOps$5ReducingSink::begin (9 bytes) inline (hot)
\-> TypeProfile (5079/5079 counts) = java/util/stream/ReduceOps$5ReducingSink
@ 32 java.util.ArrayList$ArrayListSpliterator::forEachRemaining (129 bytes) inline (hot)
@ 51 java.util.ArrayList::access$000 (5 bytes) accessor
@ 99 java.util.stream.ReferencePipeline$4$1::accept (23 bytes) inline (hot)
@ 12 bench.FindMaxInt$$Lambda$8/390011259::applyAsInt (8 bytes) inline (hot)
\-> TypeProfile (13752/13752 counts) = bench/FindMaxInt$$Lambda$8
@ 4 java.lang.Integer::intValue (5 bytes) accessor
@ 17 java.util.stream.ReduceOps$5ReducingSink::accept (19 bytes) inline (hot)
\-> TypeProfile (13752/13752 counts) = java/util/stream/ReduceOps$5ReducingSink
@ 10 bench.FindMaxInt$$Lambda$9/208515840::applyAsInt (6 bytes) inline (hot)
\-> TypeProfile (9107/9107 counts) = bench/FindMaxInt$$Lambda$9
@ 2 bench.FindMaxInt::lambda$streamWithLambda$0 (6 bytes) inline (hot)
@ 2 java.lang.Integer::max (6 bytes) inlining too deep
@ 38 java.util.stream.Sink$ChainedReference::end (10 bytes) inline (hot)
@ 4 java.util.stream.Sink::end (1 bytes) inline (hot)
\-> TypeProfile (5125/5125 counts) = java/util/stream/ReduceOps$5ReducingSink
@ 12 java.util.stream.ReduceOps$5ReducingSink::get (5 bytes) inline (hot)
@ 1 java.util.stream.ReduceOps$5ReducingSink::get (8 bytes) inline (hot)
@ 4 java.lang.Integer::valueOf (32 bytes) inline (hot)
@ 28 java.lang.Integer::<init> (10 bytes) inline (hot)
@ 1 java.lang.Number::<init> (5 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 12 java.lang.Integer::intValue (5 bytes) accessor
@ 34 org.openjdk.jmh.infra.Blackhole::consume (28 bytes) disallowed by CompilerOracle
Run Code Online (Sandbox Code Playgroud)
关键线如下。它们表示内联恰好在最终调用时中断Integer.max,因为达到了9个级别的默认限制。
@ 2 bench.FindMaxInt::lambda$streamWithLambda$0 (6 bytes) inline (hot)
@ 2 java.lang.Integer::max (6 bytes) inlining too deep
Run Code Online (Sandbox Code Playgroud)
内联树的形状在JDK 11上非常不同:
1588 705 4 bench.FindMaxInt::streamWithLambda (38 bytes)
@ 4 java.util.Collection::stream (11 bytes) inline (hot)
\-> TypeProfile (5263/5263 counts) = java/util/ArrayList
@ 1 java.util.ArrayList::spliterator (12 bytes) inline (hot)
@ 8 java.util.ArrayList$ArrayListSpliterator::<init> (26 bytes) inline (hot)
@ 6 java.lang.Object::<init> (1 bytes) inline (hot)
@ 7 java.util.stream.StreamSupport::stream (19 bytes) inline (hot)
@ 1 java.util.Objects::requireNonNull (14 bytes) inline (hot)
@ 11 java.util.stream.StreamOpFlag::fromCharacteristics (37 bytes) inline (hot)
@ 1 java.util.ArrayList$ArrayListSpliterator::characteristics (4 bytes) inline (hot)
\-> TypeProfile (5125/5125 counts) = java/util/ArrayList$ArrayListSpliterator
@ 15 java.util.stream.ReferencePipeline$Head::<init> (8 bytes) inline (hot)
@ 4 java.util.stream.ReferencePipeline::<init> (8 bytes) inline (hot)
@ 4 java.util.stream.AbstractPipeline::<init> (55 bytes) inline (hot)
@ 1 java.util.stream.PipelineHelper::<init> (5 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 9 java.lang.invoke.Invokers$Holder::linkToTargetMethod (8 bytes) force inline by annotation
@ 4 java.lang.invoke.LambdaForm$MH/0x0000000800060440::invoke (8 bytes) force inline by annotation
@ 14 java.util.stream.ReferencePipeline::mapToInt (26 bytes) inline (hot)
\-> TypeProfile (5263/5263 counts) = java/util/stream/ReferencePipeline$Head
@ 1 java.util.Objects::requireNonNull (14 bytes) inline (hot)
@ 22 java.util.stream.ReferencePipeline$4::<init> (20 bytes) inline (hot)
@ 16 java.util.stream.IntPipeline$StatelessOp::<init> (29 bytes) inline (hot)
@ 3 java.util.stream.IntPipeline::<init> (7 bytes) inline (hot)
@ 3 java.util.stream.AbstractPipeline::<init> (91 bytes) inline (hot)
@ 1 java.util.stream.PipelineHelper::<init> (5 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 51 java.util.stream.StreamOpFlag::combineOpFlags (9 bytes) inline (hot)
@ 2 java.util.stream.StreamOpFlag::getMask (30 bytes) inline (hot)
@ 66 java.util.stream.IntPipeline$StatelessOp::opIsStateful (2 bytes) inline (hot)
@ 21 java.lang.invoke.Invokers$Holder::linkToTargetMethod (8 bytes) force inline by annotation
@ 4 java.lang.invoke.LambdaForm$MH/0x0000000800060440::invoke (8 bytes) force inline by annotation
@ 26 java.util.stream.IntPipeline::reduce (16 bytes) inline (hot)
\-> TypeProfile (5263/5263 counts) = java/util/stream/ReferencePipeline$4
@ 3 java.util.stream.ReduceOps::makeInt (18 bytes) inline (hot)
@ 1 java.util.Objects::requireNonNull (14 bytes) inline (hot)
@ 14 java.util.stream.ReduceOps$6::<init> (16 bytes) inline (hot)
@ 12 java.util.stream.ReduceOps$ReduceOp::<init> (10 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 6 java.util.stream.AbstractPipeline::evaluate (94 bytes) inline (hot)
@ 50 java.util.stream.AbstractPipeline::isParallel (8 bytes) inline (hot)
@ 80 java.util.stream.TerminalOp::getOpFlags (2 bytes) inline (hot)
\-> TypeProfile (5362/5362 counts) = java/util/stream/ReduceOps$6
@ 85 java.util.stream.AbstractPipeline::sourceSpliterator (265 bytes) inline (hot)
@ 79 java.util.stream.AbstractPipeline::isParallel (8 bytes) inline (hot)
@ 88 java.util.stream.ReduceOps$ReduceOp::evaluateSequential (18 bytes) already compiled into a big method
@ 12 java.lang.Integer::intValue (5 bytes) accessor
@ 34 org.openjdk.jmh.infra.Blackhole::consume (28 bytes) disallowed by CompileCommand
Run Code Online (Sandbox Code Playgroud)
编译树由于不同的原因而更早地关闭:
@ 88 java.util.stream.ReduceOps$ReduceOp::evaluateSequential (18 bytes) already compiled into a big method
Run Code Online (Sandbox Code Playgroud)
在JDK 11中,默认的垃圾回收器已更改为G1。由于G1障碍,编译后的代码显得更大,这就是为什么内联启发式方法阻止最热的forEachRemaining循环内联到streamWithLambda方法中的原因。
实际上,这不是JDK 11中的优化,而更像是反过来。但是,该特定基准的总体性能似乎更好,因为内联树截止发生在最热的循环之外。
| 归档时间: |
|
| 查看次数: |
1120 次 |
| 最近记录: |