mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-08 16:04:10 -08:00
Improve performance of dependency analyzer, decrease memory consumption
This commit is contained in:
parent
d20accc20c
commit
f5c6ac9c5d
|
@ -89,6 +89,7 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
Map<MethodReference, DependencyPlugin> dependencyPlugins = new HashMap<>();
|
||||
private boolean completing;
|
||||
private Map<String, DependencyTypeFilter> superClassFilters = new HashMap<>();
|
||||
private List<DependencyNode> allNodes = new ArrayList<>();
|
||||
boolean asyncSupported;
|
||||
|
||||
public DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
||||
|
@ -153,8 +154,13 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
return createNode(null);
|
||||
}
|
||||
|
||||
private DependencyNode createNode(ValueType typeFilter) {
|
||||
return new DependencyNode(this, typeFilter);
|
||||
DependencyNode createNode(ValueType typeFilter) {
|
||||
if (typeFilter != null && typeFilter.isObject("java.lang.Object")) {
|
||||
typeFilter = null;
|
||||
}
|
||||
DependencyNode node = new DependencyNode(this, typeFilter);
|
||||
allNodes.add(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -266,8 +272,12 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
}
|
||||
|
||||
void schedulePropagation(DependencyNodeToNodeTransition consumer, DependencyType type) {
|
||||
if (!consumer.destination.filter(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (consumer.pendingTypes == null && propagationDepth < PROPAGATION_STACK_THRESHOLD
|
||||
&& consumer.pointsToDomainOrigin()) {
|
||||
&& consumer.pointsToDomainOrigin() && consumer.destination.propagateCount < 20) {
|
||||
++propagationDepth;
|
||||
consumer.consume(type);
|
||||
--propagationDepth;
|
||||
|
@ -290,15 +300,16 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
}
|
||||
|
||||
if (consumer.pendingTypes == null && propagationDepth < PROPAGATION_STACK_THRESHOLD
|
||||
&& consumer.pointsToDomainOrigin()) {
|
||||
&& consumer.pointsToDomainOrigin() && consumer.destination.propagateCount < 20) {
|
||||
++propagationDepth;
|
||||
consumer.consume(types);
|
||||
--propagationDepth;
|
||||
} else {
|
||||
if (consumer.pendingTypes == null) {
|
||||
pendingTransitions.add(consumer);
|
||||
consumer.pendingTypes = new IntHashSet(50);
|
||||
consumer.pendingTypes = new IntHashSet(Math.max(50, types.length));
|
||||
}
|
||||
consumer.pendingTypes.ensureCapacity(types.length + consumer.pendingTypes.size());
|
||||
for (DependencyType type : types) {
|
||||
consumer.pendingTypes.add(type.index);
|
||||
}
|
||||
|
@ -650,38 +661,44 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
|
||||
private void reportDependencies() {
|
||||
List<ReportEntry> report = new ArrayList<>();
|
||||
for (MethodReference reachableMethod : getReachableMethods()) {
|
||||
MethodDependency dependency = getMethod(reachableMethod);
|
||||
if (dependency == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= reachableMethod.parameterCount(); ++i) {
|
||||
DependencyNode param = dependency.getVariable(i);
|
||||
if (param == null) {
|
||||
continue;
|
||||
}
|
||||
report.add(new ReportEntry(reachableMethod + " param " + i, param.getTypes().length));
|
||||
}
|
||||
|
||||
if (dependency.getResult() != null) {
|
||||
report.add(new ReportEntry(reachableMethod + " result", dependency.getResult().getTypes().length));
|
||||
int domainCount = 0;
|
||||
for (DependencyNode node : allNodes) {
|
||||
String tag = node.tag + "";
|
||||
if (node.typeSet != null && node.typeSet.origin == node) {
|
||||
++domainCount;
|
||||
tag += "{*}";
|
||||
}
|
||||
report.add(new ReportEntry(tag, node.getTypes().length));
|
||||
}
|
||||
|
||||
report.sort(Comparator.comparingInt(n -> -n.count));
|
||||
for (ReportEntry entry : report) {
|
||||
System.out.println(entry.title + ": " + entry.count);
|
||||
}
|
||||
|
||||
System.out.println("Total nodes: " + allNodes.size());
|
||||
System.out.println("Total domains: " + domainCount);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
for (DependencyNode node : allNodes) {
|
||||
node.followers = null;
|
||||
node.transitions = null;
|
||||
node.transitionList = null;
|
||||
node.method = null;
|
||||
if (node.typeSet != null) {
|
||||
node.typeSet.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
for (MethodReference reachableMethod : getReachableMethods()) {
|
||||
MethodDependency dependency = getMethod(reachableMethod);
|
||||
for (int i = dependency.getParameterCount() + 1; i < dependency.getVariableCount(); ++i) {
|
||||
dependency.variableNodes[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
allNodes.clear();
|
||||
}
|
||||
|
||||
static class ReportEntry {
|
||||
|
|
|
@ -49,15 +49,11 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
ValueType typeFilter;
|
||||
private DependencyTypeFilter cachedTypeFilter;
|
||||
|
||||
boolean visitedFlag;
|
||||
int splitCount;
|
||||
public int propagateCount;
|
||||
|
||||
DependencyNode(DependencyAnalyzer dependencyAnalyzer, ValueType typeFilter) {
|
||||
this(dependencyAnalyzer, typeFilter, 0);
|
||||
}
|
||||
|
||||
private DependencyNode(DependencyAnalyzer dependencyAnalyzer, ValueType typeFilter, int degree) {
|
||||
this.dependencyAnalyzer = dependencyAnalyzer;
|
||||
this.degree = degree;
|
||||
this.typeFilter = typeFilter;
|
||||
}
|
||||
|
||||
|
@ -65,7 +61,8 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
if (degree > DEGREE_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
if (!hasType(type)) {
|
||||
if (!hasType(type) && filter(type)) {
|
||||
propagateCount++;
|
||||
moveToSeparateDomain();
|
||||
typeSet.addType(type);
|
||||
scheduleSingleType(type, null);
|
||||
|
@ -108,11 +105,19 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
return;
|
||||
}
|
||||
|
||||
if (newTypes.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (newTypes.length == 1) {
|
||||
propagate(newTypes[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
boolean copied = false;
|
||||
for (int i = 0; i < newTypes.length; ++i) {
|
||||
DependencyType type = newTypes[i];
|
||||
if (!hasType(type)) {
|
||||
if (!hasType(type) && filter(type)) {
|
||||
newTypes[j++] = type;
|
||||
} else if (!copied) {
|
||||
copied = true;
|
||||
|
@ -128,6 +133,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
return;
|
||||
}
|
||||
|
||||
propagateCount++;
|
||||
if (j < newTypes.length) {
|
||||
newTypes = Arrays.copyOf(newTypes, j);
|
||||
}
|
||||
|
@ -222,7 +228,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean filter(DependencyType type) {
|
||||
boolean filter(DependencyType type) {
|
||||
if (typeFilter == null) {
|
||||
return true;
|
||||
}
|
||||
|
@ -279,14 +285,22 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
|
||||
if (typeSet != null) {
|
||||
if (typeSet == node.typeSet) {
|
||||
typeSet.loopNodes = null;
|
||||
return;
|
||||
}
|
||||
if (typeSet.transitions != null) {
|
||||
typeSet.transitions.add(transition);
|
||||
}
|
||||
|
||||
node.propagate(filter != null ? getTypesInternal(filter) : getTypesInternal());
|
||||
DependencyType[] types = node.typeSet == null && filter == null && node.typeFilter == null
|
||||
? getTypesInternal()
|
||||
: getTypesInternal(filter, this, node);
|
||||
if (types.length > 0) {
|
||||
if (node.typeSet == null) {
|
||||
node.propagate(types);
|
||||
} else {
|
||||
dependencyAnalyzer.schedulePropagation(transition, types);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connectArrayItemNodes(node);
|
||||
|
@ -372,10 +386,8 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
ValueType itemTypeFilter = typeFilter instanceof ValueType.Array
|
||||
? ((ValueType.Array) typeFilter).getItemType()
|
||||
: null;
|
||||
if (itemTypeFilter != null && itemTypeFilter.isObject("java.lang.Object")) {
|
||||
itemTypeFilter = null;
|
||||
}
|
||||
arrayItemNode = new DependencyNode(dependencyAnalyzer, itemTypeFilter, degree + 1);
|
||||
arrayItemNode = dependencyAnalyzer.createNode(itemTypeFilter);
|
||||
arrayItemNode.degree = degree + 1;
|
||||
arrayItemNode.method = method;
|
||||
if (DependencyAnalyzer.shouldTag) {
|
||||
arrayItemNode.tag = tag + "[";
|
||||
|
@ -387,7 +399,8 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
@Override
|
||||
public DependencyNode getClassValueNode() {
|
||||
if (classValueNode == null) {
|
||||
classValueNode = new DependencyNode(dependencyAnalyzer, null, degree);
|
||||
classValueNode = dependencyAnalyzer.createNode();
|
||||
classValueNode.degree = degree;
|
||||
classValueNode.classValueNode = classValueNode;
|
||||
classValueNode.classNodeParent = this;
|
||||
if (DependencyAnalyzer.shouldTag) {
|
||||
|
@ -445,19 +458,12 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
return i == result.length ? result : Arrays.copyOf(result, i);
|
||||
}
|
||||
|
||||
DependencyType[] getTypesInternal(DependencyTypeFilter filter) {
|
||||
DependencyType[] getTypesInternal(DependencyTypeFilter filter, DependencyNode sourceNode,
|
||||
DependencyNode targetNode) {
|
||||
if (typeSet == null) {
|
||||
return new DependencyType[0];
|
||||
return TypeSet.EMPTY_TYPES;
|
||||
}
|
||||
DependencyType[] types = typeSet.getTypes();
|
||||
DependencyType[] result = new DependencyType[types.length];
|
||||
int i = 0;
|
||||
for (DependencyType type : types) {
|
||||
if (filter(type) && filter.match(type)) {
|
||||
result[i++] = type;
|
||||
}
|
||||
}
|
||||
return i == result.length ? result : Arrays.copyOf(result, i);
|
||||
return typeSet.getTypesForNode(sourceNode, targetNode, filter);
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
|
@ -480,7 +486,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
return;
|
||||
}
|
||||
|
||||
if (typeSet.origin == this || typeSet.reachesOrigin(this)) {
|
||||
if (typeSet.origin == this) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -497,12 +503,13 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
|
||||
for (DependencyNode node : domain) {
|
||||
node.typeSet = typeSet;
|
||||
node.splitCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Collection<DependencyNode> findDomain() {
|
||||
Set<DependencyNode> visited = new LinkedHashSet<>();
|
||||
Deque<DependencyNode> stack = new ArrayDeque<>();
|
||||
Set<DependencyNode> visited = new LinkedHashSet<>(50);
|
||||
Deque<DependencyNode> stack = new ArrayDeque<>(50);
|
||||
stack.push(this);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
|
@ -510,6 +517,9 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
if (!visited.add(node)) {
|
||||
continue;
|
||||
}
|
||||
if (visited.size() > 100) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (node.transitions != null) {
|
||||
for (ObjectCursor<DependencyNodeToNodeTransition> cursor : node.transitionList) {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package org.teavm.dependency;
|
||||
|
||||
import com.carrotsearch.hppc.IntSet;
|
||||
import com.carrotsearch.hppc.IntHashSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
|
@ -27,7 +27,7 @@ class DependencyNodeToNodeTransition {
|
|||
DependencyNode destination;
|
||||
DependencyTypeFilter filter;
|
||||
private BitSet knownFilteredOffTypes;
|
||||
IntSet pendingTypes;
|
||||
IntHashSet pendingTypes;
|
||||
byte destSubsetOfSrc;
|
||||
|
||||
DependencyNodeToNodeTransition(DependencyNode source, DependencyNode destination, DependencyTypeFilter filter) {
|
||||
|
@ -37,7 +37,7 @@ class DependencyNodeToNodeTransition {
|
|||
}
|
||||
|
||||
void consume(DependencyType type) {
|
||||
if (!destination.hasType(type) && filterType(type)) {
|
||||
if (!destination.hasType(type) && filterType(type) && destination.filter(type)) {
|
||||
propagate(type);
|
||||
}
|
||||
}
|
||||
|
@ -85,8 +85,17 @@ class DependencyNodeToNodeTransition {
|
|||
if (destination.typeSet == null) {
|
||||
return true;
|
||||
}
|
||||
if (destination.typeSet == source.typeSet || destination.typeSet.origin == destination
|
||||
|| destination.typeSet.typeCount() >= source.typeSet.typeCount()) {
|
||||
if (destination.typeSet == source.typeSet || destination.typeSet.origin == source
|
||||
|| destination.typeSet.typeCount() > source.typeSet.typeCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (destination.splitCount > 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (destination.typeSet.typeCount() == source.typeSet.typeCount()
|
||||
&& destination.typeSet.origin != destination) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -106,7 +115,7 @@ class DependencyNodeToNodeTransition {
|
|||
if (filter == null) {
|
||||
for (DependencyType type : types) {
|
||||
boolean added = false;
|
||||
if (!destination.hasType(type)) {
|
||||
if (!destination.hasType(type) && destination.filter(type)) {
|
||||
types[j++] = type;
|
||||
added = true;
|
||||
}
|
||||
|
@ -119,11 +128,9 @@ class DependencyNodeToNodeTransition {
|
|||
} else {
|
||||
for (DependencyType type : types) {
|
||||
boolean added = false;
|
||||
if (filterType(type)) {
|
||||
if (!destination.hasType(type)) {
|
||||
types[j++] = type;
|
||||
added = true;
|
||||
}
|
||||
if (filterType(type) && !destination.hasType(type) && destination.filter(type)) {
|
||||
types[j++] = type;
|
||||
added = true;
|
||||
}
|
||||
if (!added && !copied) {
|
||||
copied = true;
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
package org.teavm.dependency;
|
||||
|
||||
import com.carrotsearch.hppc.ObjectArrayList;
|
||||
import com.carrotsearch.hppc.ObjectHashSet;
|
||||
import com.carrotsearch.hppc.ObjectStack;
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -28,7 +26,7 @@ import java.util.Set;
|
|||
|
||||
class TypeSet {
|
||||
private static final int SMALL_TYPES_THRESHOLD = 3;
|
||||
private static final DependencyType[] EMPTY_TYPES = new DependencyType[0];
|
||||
static final DependencyType[] EMPTY_TYPES = new DependencyType[0];
|
||||
private DependencyAnalyzer dependencyAnalyzer;
|
||||
DependencyNode origin;
|
||||
private int[] smallTypes;
|
||||
|
@ -36,7 +34,6 @@ class TypeSet {
|
|||
private int typesCount;
|
||||
|
||||
Set<DependencyNode> domain = new LinkedHashSet<>();
|
||||
ObjectHashSet<DependencyNode> loopNodes;
|
||||
ObjectArrayList<DependencyNodeToNodeTransition> transitions;
|
||||
ArrayList<ConsumerWithNode> consumers;
|
||||
|
||||
|
@ -93,6 +90,41 @@ class TypeSet {
|
|||
}
|
||||
}
|
||||
|
||||
DependencyType[] getTypesForNode(DependencyNode sourceNode, DependencyNode targetNode,
|
||||
DependencyTypeFilter filter) {
|
||||
int j = 0;
|
||||
DependencyType[] types;
|
||||
if (this.types != null) {
|
||||
types = new DependencyType[this.types.cardinality()];
|
||||
for (int index = this.types.nextSetBit(0); index >= 0; index = this.types.nextSetBit(index + 1)) {
|
||||
DependencyType type = dependencyAnalyzer.types.get(index);
|
||||
if (sourceNode.filter(type) && !targetNode.hasType(type) && targetNode.filter(type)
|
||||
&& (filter == null || filter.match(type))) {
|
||||
types[j++] = type;
|
||||
}
|
||||
}
|
||||
} else if (this.smallTypes != null) {
|
||||
types = new DependencyType[smallTypes.length];
|
||||
for (int i = 0; i < types.length; ++i) {
|
||||
DependencyType type = dependencyAnalyzer.types.get(smallTypes[i]);
|
||||
if (sourceNode.filter(type) && !targetNode.hasType(type) && targetNode.filter(type)
|
||||
&& (filter == null || filter.match(type))) {
|
||||
types[j++] = type;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return EMPTY_TYPES;
|
||||
}
|
||||
|
||||
if (j == 0) {
|
||||
return EMPTY_TYPES;
|
||||
}
|
||||
if (j < types.length) {
|
||||
types = Arrays.copyOf(types, j);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
boolean hasType(DependencyType type) {
|
||||
if (smallTypes != null) {
|
||||
for (int i = 0; i < smallTypes.length; ++i) {
|
||||
|
@ -120,7 +152,6 @@ class TypeSet {
|
|||
void invalidate() {
|
||||
transitions = null;
|
||||
consumers = null;
|
||||
loopNodes = null;
|
||||
}
|
||||
|
||||
ObjectArrayList<DependencyNodeToNodeTransition> getTransitions() {
|
||||
|
@ -153,51 +184,14 @@ class TypeSet {
|
|||
return consumers;
|
||||
}
|
||||
|
||||
boolean reachesOrigin(DependencyNode node) {
|
||||
if (loopNodes == null) {
|
||||
findLoopNodes();
|
||||
}
|
||||
return loopNodes.contains(node);
|
||||
}
|
||||
|
||||
private void findLoopNodes() {
|
||||
if (domain.size() == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (DependencyNode node : domain) {
|
||||
node.visitedFlag = false;
|
||||
}
|
||||
|
||||
loopNodes = new ObjectHashSet<>(domain.size());
|
||||
loopNodes.add(origin);
|
||||
|
||||
ObjectStack<DependencyNode> stack = new ObjectStack<>(domain.size());
|
||||
stack.push(origin);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
DependencyNode next = stack.pop();
|
||||
if (next.visitedFlag) {
|
||||
continue;
|
||||
}
|
||||
next.visitedFlag = true;
|
||||
|
||||
if (next.transitions != null) {
|
||||
for (ObjectCursor<DependencyNodeToNodeTransition> cursor : next.transitionList) {
|
||||
DependencyNodeToNodeTransition transition = cursor.value;
|
||||
if (transition.destination.typeSet == this) {
|
||||
if (!transition.destination.visitedFlag) {
|
||||
stack.push(transition.destination);
|
||||
} else if (loopNodes.contains(transition.destination)) {
|
||||
loopNodes.add(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int typeCount() {
|
||||
return smallTypes != null ? smallTypes.length : types != null ? typesCount : 0;
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
origin = null;
|
||||
domain = null;
|
||||
transitions = null;
|
||||
consumers = null;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user