Skip to content

Commit 0583f73

Browse files
committedMar 4, 2024
8323183: ClassFile API performance improvements
Reviewed-by: redestad
1 parent b69d1b5 commit 0583f73

File tree

4 files changed

+59
-51
lines changed

4 files changed

+59
-51
lines changed
 

‎src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public SplitConstantPool(ClassReader parent) {
8787
this.bsmSize = parentBsmSize;
8888
this.myEntries = new PoolEntry[8];
8989
this.myBsmEntries = new BootstrapMethodEntryImpl[8];
90+
this.doneFullScan = true;
9091
}
9192

9293
@Override
@@ -189,10 +190,15 @@ protected PoolEntry fetchElement(int index) {
189190
// So we inflate the map with whatever we've got from the parent, and
190191
// later, if we miss, we do a one-time full inflation before creating
191192
// a new entry.
192-
for (int i=1; i<parentSize; i++) {
193+
for (int i=1; i<parentSize;) {
193194
PoolEntry cpi = parent.cp[i];
194-
if (cpi != null)
195+
if (cpi == null) {
196+
doneFullScan = false;
197+
i++;
198+
} else {
195199
map.put(cpi.hashCode(), cpi.index());
200+
i += cpi.width();
201+
}
196202
}
197203
for (int i = Math.max(parentSize, 1); i < size; ) {
198204
PoolEntry cpi = myEntries[i - parentSize];

‎src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java

+23-31
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,26 +25,26 @@
2525
*/
2626
package jdk.internal.classfile.impl;
2727

28-
import java.lang.constant.ClassDesc;
29-
import java.lang.constant.MethodTypeDesc;
30-
import java.nio.ByteBuffer;
31-
import java.util.BitSet;
32-
import java.util.LinkedHashMap;
33-
import java.util.List;
34-
import java.util.stream.Collectors;
3528
import java.lang.classfile.TypeKind;
3629
import java.lang.classfile.constantpool.ConstantDynamicEntry;
3730
import java.lang.classfile.constantpool.DynamicConstantPoolEntry;
3831
import java.lang.classfile.constantpool.MemberRefEntry;
39-
import static java.lang.classfile.ClassFile.*;
32+
import java.lang.constant.MethodTypeDesc;
33+
import java.nio.ByteBuffer;
34+
import java.util.ArrayDeque;
35+
import java.util.BitSet;
36+
import java.util.List;
37+
import java.util.Queue;
4038

39+
import static java.lang.classfile.ClassFile.*;
4140

4241
public final class StackCounter {
4342

43+
private record Target(int bci, int stack) {}
44+
4445
static StackCounter of(DirectCodeBuilder dcb, BufWriterImpl buf) {
4546
return new StackCounter(
4647
dcb,
47-
buf.thisClass().asSymbol(),
4848
dcb.methodInfo.methodName().stringValue(),
4949
dcb.methodInfo.methodTypeSymbol(),
5050
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
@@ -59,12 +59,12 @@ static StackCounter of(DirectCodeBuilder dcb, BufWriterImpl buf) {
5959
private final String methodName;
6060
private final MethodTypeDesc methodDesc;
6161
private final SplitConstantPool cp;
62-
private final LinkedHashMap<Integer, Integer> map;
62+
private final Queue<Target> targets;
6363
private final BitSet visited;
6464

6565
private void jump(int targetBci) {
6666
if (!visited.get(targetBci)) {
67-
map.put(targetBci, stack);
67+
targets.add(new Target(targetBci, stack));
6868
}
6969
}
7070

@@ -78,13 +78,11 @@ private void ensureLocalSlot(int index) {
7878
}
7979

8080
private boolean next() {
81-
var it = map.entrySet().iterator();
82-
while (it.hasNext()) {
83-
var en = it.next();
84-
it.remove();
85-
if (!visited.get(en.getKey())) {
86-
bcs.nextBci = en.getKey();
87-
stack = en.getValue();
81+
Target en;
82+
while ((en = targets.poll()) != null) {
83+
if (!visited.get(en.bci)) {
84+
bcs.nextBci = en.bci;
85+
stack = en.stack;
8886
return true;
8987
}
9088
}
@@ -93,7 +91,6 @@ private boolean next() {
9391
}
9492

9593
public StackCounter(LabelContext labelContext,
96-
ClassDesc thisClass,
9794
String methodName,
9895
MethodTypeDesc methodDesc,
9996
boolean isStatic,
@@ -103,16 +100,14 @@ public StackCounter(LabelContext labelContext,
103100
this.methodName = methodName;
104101
this.methodDesc = methodDesc;
105102
this.cp = cp;
106-
map = new LinkedHashMap<>();
103+
targets = new ArrayDeque<>();
107104
maxStack = stack = rets = 0;
108-
for (var h : handlers) map.put(labelContext.labelToBci(h.handler), 1);
105+
for (var h : handlers) targets.add(new Target(labelContext.labelToBci(h.handler), 1));
109106
maxLocals = isStatic ? 0 : 1;
110-
for (var cd : methodDesc.parameterList()) {
111-
maxLocals += Util.slotSize(cd);
112-
}
107+
maxLocals += Util.parameterSlots(methodDesc);
113108
bcs = new RawBytecodeHelper(bytecode);
114109
visited = new BitSet(bcs.endBci);
115-
map.put(0, 0);
110+
targets.add(new Target(0, 0));
116111
while (next()) {
117112
while (!bcs.isLastBytecode()) {
118113
bcs.rawNext();
@@ -307,14 +302,11 @@ public StackCounter(LabelContext labelContext,
307302
case INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE, INVOKEDYNAMIC -> {
308303
var cpe = cp.entryByIndex(bcs.getIndexU2());
309304
var nameAndType = opcode == INVOKEDYNAMIC ? ((DynamicConstantPoolEntry)cpe).nameAndType() : ((MemberRefEntry)cpe).nameAndType();
310-
var mDesc = MethodTypeDesc.ofDescriptor(nameAndType.type().stringValue());
311-
for (var arg : mDesc.parameterList()) {
312-
addStackSlot(-TypeKind.from(arg).slotSize());
313-
}
305+
var mtd = Util.methodTypeSymbol(nameAndType);
306+
addStackSlot(Util.slotSize(mtd.returnType()) - Util.parameterSlots(mtd));
314307
if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) {
315308
addStackSlot(-1);
316309
}
317-
addStackSlot(TypeKind.from(mDesc.returnType()).slotSize());
318310
}
319311
case MULTIANEWARRAY ->
320312
addStackSlot (1 - bcs.getU1(bcs.bci + 3));

‎src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ public static List<VerificationTypeInfo> initFrameLocals(ClassEntry thisClass, S
8080
} else {
8181
vtis = new VerificationTypeInfo[methodType.parameterCount()];
8282
}
83-
for(var arg : methodType.parameterList()) {
83+
for (int pi = 0; pi < methodType.parameterCount(); pi++) {
84+
var arg = methodType.parameterType(pi);
8485
vtis[i++] = switch (arg.descriptorString().charAt(0)) {
8586
case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.ITEM_INTEGER;
8687
case 'J' -> SimpleVerificationTypeInfo.ITEM_LONG;

‎test/micro/org/openjdk/bench/jdk/classfile/GenerateStackMaps.java ‎test/micro/org/openjdk/bench/jdk/classfile/CodeAttributeTools.java

+26-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -24,7 +24,6 @@
2424

2525
import java.io.IOException;
2626
import java.lang.constant.ClassDesc;
27-
import java.lang.constant.MethodTypeDesc;
2827
import java.net.URI;
2928
import java.nio.ByteBuffer;
3029
import java.nio.file.FileSystems;
@@ -34,12 +33,16 @@
3433
import java.util.List;
3534
import java.lang.classfile.ClassFile;
3635
import java.lang.classfile.ClassReader;
36+
import java.lang.classfile.MethodModel;
37+
import java.lang.classfile.constantpool.ConstantPool;
3738
import java.lang.classfile.constantpool.ConstantPoolBuilder;
39+
import java.lang.constant.MethodTypeDesc;
3840
import jdk.internal.classfile.impl.AbstractPseudoInstruction;
3941
import jdk.internal.classfile.impl.CodeImpl;
4042
import jdk.internal.classfile.impl.LabelContext;
4143
import jdk.internal.classfile.impl.ClassFileImpl;
4244
import jdk.internal.classfile.impl.SplitConstantPool;
45+
import jdk.internal.classfile.impl.StackCounter;
4346
import jdk.internal.classfile.impl.StackMapGenerator;
4447
import org.openjdk.jmh.annotations.Benchmark;
4548
import org.openjdk.jmh.annotations.BenchmarkMode;
@@ -51,15 +54,16 @@
5154
import org.openjdk.jmh.annotations.Setup;
5255
import org.openjdk.jmh.annotations.State;
5356
import org.openjdk.jmh.annotations.Warmup;
57+
import org.openjdk.jmh.infra.Blackhole;
5458

5559
@BenchmarkMode(Mode.Throughput)
5660
@State(Scope.Benchmark)
5761
@Fork(value = 1, jvmArgsAppend = {
5862
"--enable-preview",
5963
"--add-exports", "java.base/jdk.internal.classfile.impl=ALL-UNNAMED"})
6064
@Warmup(iterations = 2)
61-
@Measurement(iterations = 10)
62-
public class GenerateStackMaps {
65+
@Measurement(iterations = 8)
66+
public class CodeAttributeTools {
6367

6468
record GenData(LabelContext labelContext,
6569
ClassDesc thisClass,
@@ -71,17 +75,13 @@ record GenData(LabelContext labelContext,
7175
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {}
7276

7377
List<GenData> data;
74-
Iterator<GenData> it;
75-
GenData d;
76-
ClassFile cc;
7778

78-
@Setup(Level.Trial)
79+
@Setup(Level.Invocation)
7980
public void setup() throws IOException {
80-
cc = ClassFile.of();
8181
data = new ArrayList<>();
8282
Files.walk(FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java")).forEach(p -> {
8383
if (Files.isRegularFile(p) && p.toString().endsWith(".class")) try {
84-
var clm = cc.parse(p);
84+
var clm = ClassFile.of().parse(p);
8585
var thisCls = clm.thisClass().asSymbol();
8686
var cp = new SplitConstantPool((ClassReader)clm.constantPool());
8787
for (var m : clm.methods()) {
@@ -105,19 +105,28 @@ public void setup() throws IOException {
105105
}
106106

107107
@Benchmark
108-
public void benchmark() {
109-
if (it == null || !it.hasNext())
110-
it = data.iterator();
111-
var d = it.next();
112-
new StackMapGenerator(
108+
public void benchmarkStackMapsGenerator(Blackhole bh) {
109+
for (var d : data) bh.consume(new StackMapGenerator(
113110
d.labelContext(),
114111
d.thisClass(),
115112
d.methodName(),
116113
d.methodDesc(),
117114
d.isStatic(),
118115
d.bytecode().rewind(),
119116
(SplitConstantPool)d.constantPool(),
120-
(ClassFileImpl)cc,
121-
d.handlers());
117+
(ClassFileImpl)ClassFile.of(),
118+
d.handlers()));
119+
}
120+
121+
@Benchmark
122+
public void benchmarkStackCounter(Blackhole bh) {
123+
for (var d : data) bh.consume(new StackCounter(
124+
d.labelContext(),
125+
d.methodName(),
126+
d.methodDesc(),
127+
d.isStatic(),
128+
d.bytecode().rewind(),
129+
(SplitConstantPool)d.constantPool(),
130+
d.handlers()));
122131
}
123132
}

0 commit comments

Comments
 (0)
Please sign in to comment.