Improve performance of dependency analyzer, decrease memory consumption

This commit is contained in:
Alexey Andreev 2018-08-29 11:27:49 +03:00
parent d20accc20c
commit f5c6ac9c5d
4 changed files with 139 additions and 111 deletions

View File

@ -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 {

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}
}