mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8323183: ClassFile API performance improvements
Reviewed-by: redestad
This commit is contained in:
parent
b69d1b51c7
commit
0583f73574
4 changed files with 59 additions and 51 deletions
|
@ -87,6 +87,7 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
||||||
this.bsmSize = parentBsmSize;
|
this.bsmSize = parentBsmSize;
|
||||||
this.myEntries = new PoolEntry[8];
|
this.myEntries = new PoolEntry[8];
|
||||||
this.myBsmEntries = new BootstrapMethodEntryImpl[8];
|
this.myBsmEntries = new BootstrapMethodEntryImpl[8];
|
||||||
|
this.doneFullScan = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
// 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
|
// later, if we miss, we do a one-time full inflation before creating
|
||||||
// a new entry.
|
// a new entry.
|
||||||
for (int i=1; i<parentSize; i++) {
|
for (int i=1; i<parentSize;) {
|
||||||
PoolEntry cpi = parent.cp[i];
|
PoolEntry cpi = parent.cp[i];
|
||||||
if (cpi != null)
|
if (cpi == null) {
|
||||||
|
doneFullScan = false;
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
map.put(cpi.hashCode(), cpi.index());
|
map.put(cpi.hashCode(), cpi.index());
|
||||||
|
i += cpi.width();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (int i = Math.max(parentSize, 1); i < size; ) {
|
for (int i = Math.max(parentSize, 1); i < size; ) {
|
||||||
PoolEntry cpi = myEntries[i - parentSize];
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,26 +25,26 @@
|
||||||
*/
|
*/
|
||||||
package jdk.internal.classfile.impl;
|
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.TypeKind;
|
||||||
import java.lang.classfile.constantpool.ConstantDynamicEntry;
|
import java.lang.classfile.constantpool.ConstantDynamicEntry;
|
||||||
import java.lang.classfile.constantpool.DynamicConstantPoolEntry;
|
import java.lang.classfile.constantpool.DynamicConstantPoolEntry;
|
||||||
import java.lang.classfile.constantpool.MemberRefEntry;
|
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.*;
|
import static java.lang.classfile.ClassFile.*;
|
||||||
|
|
||||||
|
|
||||||
public final class StackCounter {
|
public final class StackCounter {
|
||||||
|
|
||||||
|
private record Target(int bci, int stack) {}
|
||||||
|
|
||||||
static StackCounter of(DirectCodeBuilder dcb, BufWriterImpl buf) {
|
static StackCounter of(DirectCodeBuilder dcb, BufWriterImpl buf) {
|
||||||
return new StackCounter(
|
return new StackCounter(
|
||||||
dcb,
|
dcb,
|
||||||
buf.thisClass().asSymbol(),
|
|
||||||
dcb.methodInfo.methodName().stringValue(),
|
dcb.methodInfo.methodName().stringValue(),
|
||||||
dcb.methodInfo.methodTypeSymbol(),
|
dcb.methodInfo.methodTypeSymbol(),
|
||||||
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
|
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
|
||||||
|
@ -59,12 +59,12 @@ public final class StackCounter {
|
||||||
private final String methodName;
|
private final String methodName;
|
||||||
private final MethodTypeDesc methodDesc;
|
private final MethodTypeDesc methodDesc;
|
||||||
private final SplitConstantPool cp;
|
private final SplitConstantPool cp;
|
||||||
private final LinkedHashMap<Integer, Integer> map;
|
private final Queue<Target> targets;
|
||||||
private final BitSet visited;
|
private final BitSet visited;
|
||||||
|
|
||||||
private void jump(int targetBci) {
|
private void jump(int targetBci) {
|
||||||
if (!visited.get(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() {
|
private boolean next() {
|
||||||
var it = map.entrySet().iterator();
|
Target en;
|
||||||
while (it.hasNext()) {
|
while ((en = targets.poll()) != null) {
|
||||||
var en = it.next();
|
if (!visited.get(en.bci)) {
|
||||||
it.remove();
|
bcs.nextBci = en.bci;
|
||||||
if (!visited.get(en.getKey())) {
|
stack = en.stack;
|
||||||
bcs.nextBci = en.getKey();
|
|
||||||
stack = en.getValue();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +91,6 @@ public final class StackCounter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public StackCounter(LabelContext labelContext,
|
public StackCounter(LabelContext labelContext,
|
||||||
ClassDesc thisClass,
|
|
||||||
String methodName,
|
String methodName,
|
||||||
MethodTypeDesc methodDesc,
|
MethodTypeDesc methodDesc,
|
||||||
boolean isStatic,
|
boolean isStatic,
|
||||||
|
@ -103,16 +100,14 @@ public final class StackCounter {
|
||||||
this.methodName = methodName;
|
this.methodName = methodName;
|
||||||
this.methodDesc = methodDesc;
|
this.methodDesc = methodDesc;
|
||||||
this.cp = cp;
|
this.cp = cp;
|
||||||
map = new LinkedHashMap<>();
|
targets = new ArrayDeque<>();
|
||||||
maxStack = stack = rets = 0;
|
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;
|
maxLocals = isStatic ? 0 : 1;
|
||||||
for (var cd : methodDesc.parameterList()) {
|
maxLocals += Util.parameterSlots(methodDesc);
|
||||||
maxLocals += Util.slotSize(cd);
|
|
||||||
}
|
|
||||||
bcs = new RawBytecodeHelper(bytecode);
|
bcs = new RawBytecodeHelper(bytecode);
|
||||||
visited = new BitSet(bcs.endBci);
|
visited = new BitSet(bcs.endBci);
|
||||||
map.put(0, 0);
|
targets.add(new Target(0, 0));
|
||||||
while (next()) {
|
while (next()) {
|
||||||
while (!bcs.isLastBytecode()) {
|
while (!bcs.isLastBytecode()) {
|
||||||
bcs.rawNext();
|
bcs.rawNext();
|
||||||
|
@ -307,14 +302,11 @@ public final class StackCounter {
|
||||||
case INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE, INVOKEDYNAMIC -> {
|
case INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE, INVOKEDYNAMIC -> {
|
||||||
var cpe = cp.entryByIndex(bcs.getIndexU2());
|
var cpe = cp.entryByIndex(bcs.getIndexU2());
|
||||||
var nameAndType = opcode == INVOKEDYNAMIC ? ((DynamicConstantPoolEntry)cpe).nameAndType() : ((MemberRefEntry)cpe).nameAndType();
|
var nameAndType = opcode == INVOKEDYNAMIC ? ((DynamicConstantPoolEntry)cpe).nameAndType() : ((MemberRefEntry)cpe).nameAndType();
|
||||||
var mDesc = MethodTypeDesc.ofDescriptor(nameAndType.type().stringValue());
|
var mtd = Util.methodTypeSymbol(nameAndType);
|
||||||
for (var arg : mDesc.parameterList()) {
|
addStackSlot(Util.slotSize(mtd.returnType()) - Util.parameterSlots(mtd));
|
||||||
addStackSlot(-TypeKind.from(arg).slotSize());
|
|
||||||
}
|
|
||||||
if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) {
|
if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) {
|
||||||
addStackSlot(-1);
|
addStackSlot(-1);
|
||||||
}
|
}
|
||||||
addStackSlot(TypeKind.from(mDesc.returnType()).slotSize());
|
|
||||||
}
|
}
|
||||||
case MULTIANEWARRAY ->
|
case MULTIANEWARRAY ->
|
||||||
addStackSlot (1 - bcs.getU1(bcs.bci + 3));
|
addStackSlot (1 - bcs.getU1(bcs.bci + 3));
|
||||||
|
|
|
@ -80,7 +80,8 @@ public class StackMapDecoder {
|
||||||
} else {
|
} else {
|
||||||
vtis = new VerificationTypeInfo[methodType.parameterCount()];
|
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)) {
|
vtis[i++] = switch (arg.descriptorString().charAt(0)) {
|
||||||
case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.ITEM_INTEGER;
|
case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.ITEM_INTEGER;
|
||||||
case 'J' -> SimpleVerificationTypeInfo.ITEM_LONG;
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.io.IOException;
|
||||||
import java.lang.constant.ClassDesc;
|
import java.lang.constant.ClassDesc;
|
||||||
import java.lang.constant.MethodTypeDesc;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.file.FileSystems;
|
import java.nio.file.FileSystems;
|
||||||
|
@ -34,12 +33,16 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.lang.classfile.ClassFile;
|
import java.lang.classfile.ClassFile;
|
||||||
import java.lang.classfile.ClassReader;
|
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.classfile.constantpool.ConstantPoolBuilder;
|
||||||
|
import java.lang.constant.MethodTypeDesc;
|
||||||
import jdk.internal.classfile.impl.AbstractPseudoInstruction;
|
import jdk.internal.classfile.impl.AbstractPseudoInstruction;
|
||||||
import jdk.internal.classfile.impl.CodeImpl;
|
import jdk.internal.classfile.impl.CodeImpl;
|
||||||
import jdk.internal.classfile.impl.LabelContext;
|
import jdk.internal.classfile.impl.LabelContext;
|
||||||
import jdk.internal.classfile.impl.ClassFileImpl;
|
import jdk.internal.classfile.impl.ClassFileImpl;
|
||||||
import jdk.internal.classfile.impl.SplitConstantPool;
|
import jdk.internal.classfile.impl.SplitConstantPool;
|
||||||
|
import jdk.internal.classfile.impl.StackCounter;
|
||||||
import jdk.internal.classfile.impl.StackMapGenerator;
|
import jdk.internal.classfile.impl.StackMapGenerator;
|
||||||
import org.openjdk.jmh.annotations.Benchmark;
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
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.Setup;
|
||||||
import org.openjdk.jmh.annotations.State;
|
import org.openjdk.jmh.annotations.State;
|
||||||
import org.openjdk.jmh.annotations.Warmup;
|
import org.openjdk.jmh.annotations.Warmup;
|
||||||
|
import org.openjdk.jmh.infra.Blackhole;
|
||||||
|
|
||||||
@BenchmarkMode(Mode.Throughput)
|
@BenchmarkMode(Mode.Throughput)
|
||||||
@State(Scope.Benchmark)
|
@State(Scope.Benchmark)
|
||||||
|
@ -58,8 +62,8 @@ import org.openjdk.jmh.annotations.Warmup;
|
||||||
"--enable-preview",
|
"--enable-preview",
|
||||||
"--add-exports", "java.base/jdk.internal.classfile.impl=ALL-UNNAMED"})
|
"--add-exports", "java.base/jdk.internal.classfile.impl=ALL-UNNAMED"})
|
||||||
@Warmup(iterations = 2)
|
@Warmup(iterations = 2)
|
||||||
@Measurement(iterations = 10)
|
@Measurement(iterations = 8)
|
||||||
public class GenerateStackMaps {
|
public class CodeAttributeTools {
|
||||||
|
|
||||||
record GenData(LabelContext labelContext,
|
record GenData(LabelContext labelContext,
|
||||||
ClassDesc thisClass,
|
ClassDesc thisClass,
|
||||||
|
@ -71,17 +75,13 @@ public class GenerateStackMaps {
|
||||||
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {}
|
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {}
|
||||||
|
|
||||||
List<GenData> data;
|
List<GenData> data;
|
||||||
Iterator<GenData> it;
|
|
||||||
GenData d;
|
|
||||||
ClassFile cc;
|
|
||||||
|
|
||||||
@Setup(Level.Trial)
|
@Setup(Level.Invocation)
|
||||||
public void setup() throws IOException {
|
public void setup() throws IOException {
|
||||||
cc = ClassFile.of();
|
|
||||||
data = new ArrayList<>();
|
data = new ArrayList<>();
|
||||||
Files.walk(FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java")).forEach(p -> {
|
Files.walk(FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java")).forEach(p -> {
|
||||||
if (Files.isRegularFile(p) && p.toString().endsWith(".class")) try {
|
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 thisCls = clm.thisClass().asSymbol();
|
||||||
var cp = new SplitConstantPool((ClassReader)clm.constantPool());
|
var cp = new SplitConstantPool((ClassReader)clm.constantPool());
|
||||||
for (var m : clm.methods()) {
|
for (var m : clm.methods()) {
|
||||||
|
@ -105,11 +105,8 @@ public class GenerateStackMaps {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
public void benchmark() {
|
public void benchmarkStackMapsGenerator(Blackhole bh) {
|
||||||
if (it == null || !it.hasNext())
|
for (var d : data) bh.consume(new StackMapGenerator(
|
||||||
it = data.iterator();
|
|
||||||
var d = it.next();
|
|
||||||
new StackMapGenerator(
|
|
||||||
d.labelContext(),
|
d.labelContext(),
|
||||||
d.thisClass(),
|
d.thisClass(),
|
||||||
d.methodName(),
|
d.methodName(),
|
||||||
|
@ -117,7 +114,19 @@ public class GenerateStackMaps {
|
||||||
d.isStatic(),
|
d.isStatic(),
|
||||||
d.bytecode().rewind(),
|
d.bytecode().rewind(),
|
||||||
(SplitConstantPool)d.constantPool(),
|
(SplitConstantPool)d.constantPool(),
|
||||||
(ClassFileImpl)cc,
|
(ClassFileImpl)ClassFile.of(),
|
||||||
d.handlers());
|
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…
Add table
Add a link
Reference in a new issue