Permalink
Browse files

Improve performance of RTA algorithm.

Get rid of Set, Map, MultiMap in the algorithm and ensure we unfold the same dynamic reference only one time. These modifications decrease the time spent in the algorithm itself from ~1min to ~1s

Additional changes: refactor logic of processing polymorphic reference in order to make it more readable and fix some old comment.

PiperOrigin-RevId: 233095181
  • Loading branch information...
jDramaix authored and Copybara-Service committed Feb 8, 2019
1 parent 09ed7ba commit 4bdcef276ce682472c06db7bf8ea28c0b06ca275
@@ -68,7 +68,7 @@ public static void main(String[] args) throws CmdLineException {
private void run() {
LibraryInfo libraryInfo = mergeCallGraphFiles();

RtaResult rtaResult = new RapidTypeAnalyser(libraryInfo).analyse();
RtaResult rtaResult = RapidTypeAnalyser.analyse(libraryInfo);

writeToFile(unusedTypesOutputFilePath, rtaResult.getUnusedTypes());
writeToFile(unusedMembersOutputFilePath, rtaResult.getUnusedMembers());
@@ -15,10 +15,11 @@
*/
package com.google.j2cl.tools.rta;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.google.j2cl.libraryinfo.InvocationKind;
import com.google.j2cl.libraryinfo.MemberInfo;
import java.util.ArrayList;
import java.util.List;

final class Member {
@@ -36,15 +37,21 @@ static Member buildFrom(MemberInfo memberInfo, Type declaringType) {
return member;
}

private final Multimap<InvocationKind, Member> referencedMembers = HashMultimap.create();
private String name;
private Type declaringType;
private List<Type> referencedTypes;
private boolean jsAccessible;
private boolean isStatic;
private int startLine = -1;
private int endLine = -1;

private boolean fullyTraversed;
private boolean live;
private List<Type> referencedTypes;
private final Multimap<InvocationKind, Member> referencedMembers =
MultimapBuilder.enumKeys(InvocationKind.class).arrayListValues().build();
private final List<Type> overridingTypes = new ArrayList<>();
private final List<Type> inheritingTypes = new ArrayList<>();

private Member() {}

Type getDeclaringType() {
@@ -77,12 +84,20 @@ InvocationKind getDefaultInvocationKind() {
}
}

Multimap<InvocationKind, Member> getReferencedMembers() {
return referencedMembers;
boolean isLive() {
return live;
}

void addReferencedMember(InvocationKind invocationKind, Member referencedMember) {
referencedMembers.put(invocationKind, referencedMember);
void markLive() {
this.live = true;
}

boolean isFullyTraversed() {
return fullyTraversed;
}

void markFullyTraversed() {
this.fullyTraversed = true;
}

List<Type> getReferencedTypes() {
@@ -92,4 +107,33 @@ void addReferencedMember(InvocationKind invocationKind, Member referencedMember)
void setReferencedTypes(List<Type> referencedTypes) {
this.referencedTypes = referencedTypes;
}

Multimap<InvocationKind, Member> getReferencedMembers() {
return referencedMembers;
}

void addReferencedMember(InvocationKind invocationKind, Member referencedMember) {
referencedMembers.put(invocationKind, referencedMember);
}

/** Returns the list of types overriding the implementation of this member. */
List<Type> getOverridingTypes() {
return overridingTypes;
}

void addOverridingType(Type type) {
overridingTypes.add(type);
}

/**
* Returns the list of types inheriting the implementation of this member including the enclosing
* type of the member.
*/
List<Type> getInheritingTypes() {
return inheritingTypes;
}

void addInheritingType(Type type) {
inheritingTypes.add(type);
}
}
@@ -15,45 +15,44 @@
*/
package com.google.j2cl.tools.rta;

import static com.google.common.base.Predicates.not;
import static java.util.stream.Collectors.toSet;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
import com.google.j2cl.libraryinfo.InvocationKind;
import com.google.j2cl.libraryinfo.LibraryInfo;
import java.util.HashSet;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

final class RapidTypeAnalyser {
private final Set<Type> instantiatedTypes = new HashSet<>();
private final Set<Type> unusedTypes;
private final Set<Member> unusedMembers;
private final SetMultimap<Type, Member> virtuallyLiveMemberPerType = HashMultimap.create();
private final TypeHierarchyGraph typeHierarchyGraph;

RapidTypeAnalyser(LibraryInfo libraryInfo) {
typeHierarchyGraph = TypeHierarchyGraph.buildFrom(libraryInfo);
unusedTypes = new HashSet<>(typeHierarchyGraph.getTypes());
unusedMembers =
typeHierarchyGraph.getTypes().stream()
.flatMap(t -> t.getMembers().stream())
.collect(Collectors.toSet());
}

RtaResult analyse() {
static RtaResult analyse(LibraryInfo libraryInfo) {
List<Type> types = TypeGraphBuilder.build(libraryInfo);

// Go over the entry points to start the traversal.
typeHierarchyGraph.getTypes().stream()
types.stream()
.flatMap(t -> t.getMembers().stream())
.filter(Member::isJsAccessible)
.forEach(m -> onMemberReference(m.getDefaultInvocationKind(), m));

return RtaResult.build(unusedTypes, unusedMembers);
return RtaResult.build(getUnusedTypes(types), getUnusedMembers(types));
}

private static Set<Type> getUnusedTypes(List<Type> types) {
return types.stream().filter(not(Type::isLive)).collect(toSet());
}

private void onMemberReference(InvocationKind invocationKind, Member member) {
private static Set<Member> getUnusedMembers(List<Type> types) {
return types.stream()
.flatMap(t -> t.getMembers().stream())
.filter(not(Member::isLive))
.collect(toSet());
}

private static void onMemberReference(InvocationKind invocationKind, Member member) {
switch (invocationKind) {
case DYNAMIC:
unfoldPolymorphicReference(member);
traversePolymorphicReference(member);
break;
case STATIC:
markTypeLive(member.getDeclaringType());
@@ -68,60 +67,71 @@ private void onMemberReference(InvocationKind invocationKind, Member member) {
}
}

private void onTypeReference(Type type) {
private static void onTypeReference(Type type) {
markTypeLive(type);
}

private void markMemberLive(Member member) {
if (!unusedMembers.remove(member)) {
// already live
private static void markMemberLive(Member member) {
if (member.isLive()) {
return;
}

member.getReferencedTypes().forEach(this::onTypeReference);
member.getReferencedMembers().forEach(this::onMemberReference);
}
member.markLive();

private void unfoldPolymorphicReference(Member member) {
// Set of types inheriting this member (includes the enclosing type of the member).
Set<Type> inheritingTypes = typeHierarchyGraph.getTypesInheriting(member);
member.getReferencedTypes().forEach(RapidTypeAnalyser::onTypeReference);
member.getReferencedMembers().forEach(RapidTypeAnalyser::onMemberReference);
}

if (containsLiveTypes(inheritingTypes)) {
markMemberLive(member);
} else {
// none of the possible types for this member is live. Register this member,
// so once one of the type become live, the member will be added to the live member
// graph.
inheritingTypes.forEach(c -> virtuallyLiveMemberPerType.put(c, member));
private static void traversePolymorphicReference(Member member) {
if (member.isFullyTraversed()) {
return;
}
member.markFullyTraversed();

markMemberPotentiallyLive(member);

// Recursively unfold the overriding chain.
for (Type overridingType : typeHierarchyGraph.getTypesOverriding(member)) {
for (Type overridingType : member.getOverridingTypes()) {
Member newTargetMember = overridingType.getMemberByName(member.getName());
unfoldPolymorphicReference(newTargetMember);
traversePolymorphicReference(newTargetMember);
}
}

private static void markMemberPotentiallyLive(Member member) {
// Set of types inheriting this member
List<Type> inheritingTypes = member.getInheritingTypes();

if (containsInstantiatedTypes(inheritingTypes)) {
markMemberLive(member);
} else {
// None of the types inheriting this polymorphic member is instantiated.
// Register this member so that it gets marked live once an inheriting type is instantiated.
inheritingTypes.forEach(t -> t.addPotentiallyLiveMember(member));
}
}

private void markTypeLive(Type type) {
if (!unusedTypes.remove(type)) {
// already live
private static void markTypeLive(Type type) {
if (type.isLive()) {
return;
}

type.markLive();

// When a type is marked as live, we need to mark the super types as live too because their are
// referred to in the class declaration (as supertypes or encoded as implementing interfaces).
type.getSuperTypes().forEach(this::markTypeLive);
type.getSuperTypes().forEach(RapidTypeAnalyser::markTypeLive);
}

private void instantiate(Type type) {
if (!instantiatedTypes.add(type)) {
// Type is already live
private static void instantiate(Type type) {
if (type.isInstantiated()) {
return;
}
virtuallyLiveMemberPerType.get(type).forEach(this::markMemberLive);
type.instantiate();

type.getPotentiallyLiveMembers().forEach(RapidTypeAnalyser::markMemberLive);
}

private boolean containsLiveTypes(Set<Type> types) {
return types.stream().anyMatch(instantiatedTypes::contains);
private static boolean containsInstantiatedTypes(Collection<Type> types) {
return types.stream().anyMatch(Type::isInstantiated);
}
}
@@ -19,6 +19,7 @@

import com.google.common.collect.ImmutableList;
import com.google.j2cl.libraryinfo.TypeInfo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
@@ -30,6 +31,9 @@
private final LinkedHashMap<String, Member> membersByName = new LinkedHashMap<>();
private String implSourceFile;
private String headerSourceFile;
private boolean live;
private boolean instantiated;
private final List<Member> potentiallyLiveMembers = new ArrayList<>();

static Type buildFrom(TypeInfo typeInfo) {
Type type = new Type();
@@ -92,4 +96,29 @@ void setSuperTypes(List<Type> superTypes) {
List<Type> getSuperTypes() {
return superTypes;
}

void markLive() {
this.live = true;
}

boolean isLive() {
return live;
}

boolean isInstantiated() {
return instantiated;
}

void instantiate() {
this.instantiated = true;
}

/** Returns the list of members that need to mark as live when the type becomes live. */
List<Member> getPotentiallyLiveMembers() {
return potentiallyLiveMembers;
}

void addPotentiallyLiveMember(Member member) {
potentiallyLiveMembers.add(member);
}
}
Oops, something went wrong.

0 comments on commit 4bdcef2

Please sign in to comment.