8323183: ClassFile API performance improvements
Reviewed-by: redestad
This commit is contained in:
parent
b69d1b51c7
commit
0583f73574
@ -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];
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user