8323183: ClassFile API performance improvements

Reviewed-by: redestad
This commit is contained in:
Adam Sotona 2024-03-04 15:26:27 +00:00
parent b69d1b51c7
commit 0583f73574
4 changed files with 59 additions and 51 deletions

View File

@ -87,6 +87,7 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
this.bsmSize = parentBsmSize;
this.myEntries = new PoolEntry[8];
this.myBsmEntries = new BootstrapMethodEntryImpl[8];
this.doneFullScan = true;
}
@Override
@ -189,10 +190,15 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
// So we inflate the map with whatever we've got from the parent, and
// later, if we miss, we do a one-time full inflation before creating
// a new entry.
for (int i=1; i<parentSize; i++) {
for (int i=1; i<parentSize;) {
PoolEntry cpi = parent.cp[i];
if (cpi != null)
if (cpi == null) {
doneFullScan = false;
i++;
} else {
map.put(cpi.hashCode(), cpi.index());
i += cpi.width();
}
}
for (int i = Math.max(parentSize, 1); i < size; ) {
PoolEntry cpi = myEntries[i - parentSize];

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,26 +25,26 @@
*/
package jdk.internal.classfile.impl;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.nio.ByteBuffer;
import java.util.BitSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;
import java.lang.classfile.TypeKind;
import java.lang.classfile.constantpool.ConstantDynamicEntry;
import java.lang.classfile.constantpool.DynamicConstantPoolEntry;
import java.lang.classfile.constantpool.MemberRefEntry;
import java.lang.constant.MethodTypeDesc;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.BitSet;
import java.util.List;
import java.util.Queue;
import static java.lang.classfile.ClassFile.*;
public final class StackCounter {
private record Target(int bci, int stack) {}
static StackCounter of(DirectCodeBuilder dcb, BufWriterImpl buf) {
return new StackCounter(
dcb,
buf.thisClass().asSymbol(),
dcb.methodInfo.methodName().stringValue(),
dcb.methodInfo.methodTypeSymbol(),
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
@ -59,12 +59,12 @@ public final class StackCounter {
private final String methodName;
private final MethodTypeDesc methodDesc;
private final SplitConstantPool cp;
private final LinkedHashMap<Integer, Integer> map;
private final Queue<Target> targets;
private final BitSet visited;
private void jump(int targetBci) {
if (!visited.get(targetBci)) {
map.put(targetBci, stack);
targets.add(new Target(targetBci, stack));
}
}
@ -78,13 +78,11 @@ public final class StackCounter {
}
private boolean next() {
var it = map.entrySet().iterator();
while (it.hasNext()) {
var en = it.next();
it.remove();
if (!visited.get(en.getKey())) {
bcs.nextBci = en.getKey();
stack = en.getValue();
Target en;
while ((en = targets.poll()) != null) {
if (!visited.get(en.bci)) {
bcs.nextBci = en.bci;
stack = en.stack;
return true;
}
}
@ -93,7 +91,6 @@ public final class StackCounter {
}
public StackCounter(LabelContext labelContext,
ClassDesc thisClass,
String methodName,
MethodTypeDesc methodDesc,
boolean isStatic,
@ -103,16 +100,14 @@ public final class StackCounter {
this.methodName = methodName;
this.methodDesc = methodDesc;
this.cp = cp;
map = new LinkedHashMap<>();
targets = new ArrayDeque<>();
maxStack = stack = rets = 0;
for (var h : handlers) map.put(labelContext.labelToBci(h.handler), 1);
for (var h : handlers) targets.add(new Target(labelContext.labelToBci(h.handler), 1));
maxLocals = isStatic ? 0 : 1;
for (var cd : methodDesc.parameterList()) {
maxLocals += Util.slotSize(cd);
}
maxLocals += Util.parameterSlots(methodDesc);
bcs = new RawBytecodeHelper(bytecode);
visited = new BitSet(bcs.endBci);
map.put(0, 0);
targets.add(new Target(0, 0));
while (next()) {
while (!bcs.isLastBytecode()) {
bcs.rawNext();
@ -307,14 +302,11 @@ public final class StackCounter {
case INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE, INVOKEDYNAMIC -> {
var cpe = cp.entryByIndex(bcs.getIndexU2());
var nameAndType = opcode == INVOKEDYNAMIC ? ((DynamicConstantPoolEntry)cpe).nameAndType() : ((MemberRefEntry)cpe).nameAndType();
var mDesc = MethodTypeDesc.ofDescriptor(nameAndType.type().stringValue());
for (var arg : mDesc.parameterList()) {
addStackSlot(-TypeKind.from(arg).slotSize());
}
var mtd = Util.methodTypeSymbol(nameAndType);
addStackSlot(Util.slotSize(mtd.returnType()) - Util.parameterSlots(mtd));
if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) {
addStackSlot(-1);
}
addStackSlot(TypeKind.from(mDesc.returnType()).slotSize());
}
case MULTIANEWARRAY ->
addStackSlot (1 - bcs.getU1(bcs.bci + 3));

View File

@ -80,7 +80,8 @@ public class StackMapDecoder {
} else {
vtis = new VerificationTypeInfo[methodType.parameterCount()];
}
for(var arg : methodType.parameterList()) {
for (int pi = 0; pi < methodType.parameterCount(); pi++) {
var arg = methodType.parameterType(pi);
vtis[i++] = switch (arg.descriptorString().charAt(0)) {
case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.ITEM_INTEGER;
case 'J' -> SimpleVerificationTypeInfo.ITEM_LONG;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -24,7 +24,6 @@ package org.openjdk.bench.jdk.classfile;
import java.io.IOException;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.FileSystems;
@ -34,12 +33,16 @@ import java.util.ArrayList;
import java.util.List;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassReader;
import java.lang.classfile.MethodModel;
import java.lang.classfile.constantpool.ConstantPool;
import java.lang.classfile.constantpool.ConstantPoolBuilder;
import java.lang.constant.MethodTypeDesc;
import jdk.internal.classfile.impl.AbstractPseudoInstruction;
import jdk.internal.classfile.impl.CodeImpl;
import jdk.internal.classfile.impl.LabelContext;
import jdk.internal.classfile.impl.ClassFileImpl;
import jdk.internal.classfile.impl.SplitConstantPool;
import jdk.internal.classfile.impl.StackCounter;
import jdk.internal.classfile.impl.StackMapGenerator;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
@ -51,6 +54,7 @@ import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
@BenchmarkMode(Mode.Throughput)
@State(Scope.Benchmark)
@ -58,8 +62,8 @@ import org.openjdk.jmh.annotations.Warmup;
"--enable-preview",
"--add-exports", "java.base/jdk.internal.classfile.impl=ALL-UNNAMED"})
@Warmup(iterations = 2)
@Measurement(iterations = 10)
public class GenerateStackMaps {
@Measurement(iterations = 8)
public class CodeAttributeTools {
record GenData(LabelContext labelContext,
ClassDesc thisClass,
@ -71,17 +75,13 @@ public class GenerateStackMaps {
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {}
List<GenData> data;
Iterator<GenData> it;
GenData d;
ClassFile cc;
@Setup(Level.Trial)
@Setup(Level.Invocation)
public void setup() throws IOException {
cc = ClassFile.of();
data = new ArrayList<>();
Files.walk(FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java")).forEach(p -> {
if (Files.isRegularFile(p) && p.toString().endsWith(".class")) try {
var clm = cc.parse(p);
var clm = ClassFile.of().parse(p);
var thisCls = clm.thisClass().asSymbol();
var cp = new SplitConstantPool((ClassReader)clm.constantPool());
for (var m : clm.methods()) {
@ -105,11 +105,8 @@ public class GenerateStackMaps {
}
@Benchmark
public void benchmark() {
if (it == null || !it.hasNext())
it = data.iterator();
var d = it.next();
new StackMapGenerator(
public void benchmarkStackMapsGenerator(Blackhole bh) {
for (var d : data) bh.consume(new StackMapGenerator(
d.labelContext(),
d.thisClass(),
d.methodName(),
@ -117,7 +114,19 @@ public class GenerateStackMaps {
d.isStatic(),
d.bytecode().rewind(),
(SplitConstantPool)d.constantPool(),
(ClassFileImpl)cc,
d.handlers());
(ClassFileImpl)ClassFile.of(),
d.handlers()));
}
@Benchmark
public void benchmarkStackCounter(Blackhole bh) {
for (var d : data) bh.consume(new StackCounter(
d.labelContext(),
d.methodName(),
d.methodDesc(),
d.isStatic(),
d.bytecode().rewind(),
(SplitConstantPool)d.constantPool(),
d.handlers()));
}
}