Limit the number of possible equivalence groups.
This CL hardcodes a limit on the number of actual groups for a given set
of potential equivalences to ensure that the quadratic behavior is not
hit. The expected groups should generally be small for a given type of
synthetics so this will not likely have an impact on size.
It would be beneficial to follow-up with a sorting-based implementation
and avoid the arbitrary limit.
Bug: b/237413146
Bug: b/236690153
Change-Id: Id4e57e83a5a0b1431e95753d605c52865c4b05b9
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 9e46ebb..34f4de6 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -208,6 +208,7 @@
}
Timing timing = Timing.create("D8", options);
try {
+ timing.begin("Pre conversion");
// Synthetic assertion to check that testing assertions works and can be enabled.
assert forTesting(options, () -> !options.testing.testEnableTestAssertions);
@@ -246,9 +247,9 @@
if (options.testing.enableD8ResourcesPassThrough) {
appView.setAppServices(AppServices.builder(appView).build());
}
-
+ timing.end();
new IRConverter(appView, timing, printer).convert(appView, executor);
-
+ timing.begin("Post conversion");
if (options.printCfg) {
if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
System.out.print(printer.toString());
@@ -286,9 +287,22 @@
}
Marker.checkCompatibleDesugaredLibrary(markers, options.reporter);
- InspectorImpl.runInspections(options.outputInspections, appView.appInfo().classes());
- appView.setNamingLens(PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView));
- appView.setNamingLens(RecordRewritingNamingLens.createRecordRewritingNamingLens(appView));
+ timing.time(
+ "Run inspections",
+ () ->
+ InspectorImpl.runInspections(options.outputInspections, appView.appInfo().classes()));
+
+ timing.time(
+ "Create prefix rewriting lens",
+ () ->
+ appView.setNamingLens(
+ PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView)));
+
+ timing.time(
+ "Create record rewriting lens",
+ () ->
+ appView.setNamingLens(
+ RecordRewritingNamingLens.createRecordRewritingNamingLens(appView)));
if (options.isGeneratingDex()
&& hasDexResources
@@ -313,20 +327,34 @@
// Since tracing is not lens aware, this needs to be done prior to synthetic finalization
// which will construct a graph lens.
if (options.isGeneratingDex() && !options.mainDexKeepRules.isEmpty()) {
+ timing.begin("Generate main-dex list");
appView.dexItemFactory().clearTypeElementsCache();
MainDexInfo mainDexInfo =
new GenerateMainDexList(options)
.traceMainDex(
executor, appView.appInfo().app(), appView.appInfo().getMainDexInfo());
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
+ timing.end();
}
- finalizeApplication(appView, executor);
+ timing.time("Finalize synthetics", () -> finalizeApplication(appView, executor, timing));
- HorizontalClassMerger.createForD8ClassMerging(appView).runIfNecessary(executor, timing);
+ timing.time(
+ "Horizontal merger",
+ () ->
+ HorizontalClassMerger.createForD8ClassMerging(appView)
+ .runIfNecessary(executor, timing));
- new GenericSignatureRewriter(appView).runForD8(appView.appInfo().classes(), executor);
- new KotlinMetadataRewriter(appView).runForD8(executor);
+ timing.time(
+ "Signature rewriter",
+ () ->
+ new GenericSignatureRewriter(appView)
+ .runForD8(appView.appInfo().classes(), executor));
+
+ timing.time(
+ "Kotlin metadata rewriter", () -> new KotlinMetadataRewriter(appView).runForD8(executor));
+
+ timing.end(); // post-converter
if (options.isGeneratingClassFiles()) {
new CfApplicationWriter(appView, marker).write(options.getClassFileConsumer(), inputApp);
@@ -366,9 +394,10 @@
appView.setAssumeInfoCollection(assumeInfoCollectionBuilder.build());
}
- private static void finalizeApplication(AppView<AppInfo> appView, ExecutorService executorService)
+ private static void finalizeApplication(
+ AppView<AppInfo> appView, ExecutorService executorService, Timing timing)
throws ExecutionException {
- SyntheticFinalization.finalize(appView, executorService);
+ SyntheticFinalization.finalize(appView, timing, executorService);
}
private static DexApplication rewriteNonDexInputs(
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 1e0fc7c..1dddf3f 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -137,7 +137,7 @@
new IRConverter(appView, timing).convert(appView, executor);
- SyntheticFinalization.finalize(appView, executor);
+ SyntheticFinalization.finalize(appView, timing, executor);
appView.setNamingLens(PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView));
new GenericSignatureRewriter(appView).run(appView.appInfo().classes(), executor);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index c75fbf2..b781d3a 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -718,9 +718,9 @@
appView.setGraphLens(MemberRebindingIdentityLensFactory.create(appView, executorService));
if (appView.appInfo().hasLiveness()) {
- SyntheticFinalization.finalizeWithLiveness(appView.withLiveness(), executorService);
+ SyntheticFinalization.finalizeWithLiveness(appView.withLiveness(), executorService, timing);
} else {
- SyntheticFinalization.finalizeWithClassHierarchy(appView, executorService);
+ SyntheticFinalization.finalizeWithClassHierarchy(appView, executorService, timing);
}
// Read any -applymapping input to allow for repackaging to not relocate the classes.
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 822fa38..cfb078e 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -36,6 +36,7 @@
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap;
@@ -63,6 +64,9 @@
public class SyntheticFinalization {
+ // TODO(b/237413146): Implement a non-quadratic grouping algorithm.
+ private static final int GROUP_COUNT_THRESHOLD = 10;
+
public static class Result {
public final CommittedItems commit;
public final NonIdentityGraphLens lens;
@@ -155,12 +159,13 @@
this.committed = committed;
}
- public static void finalize(AppView<AppInfo> appView, ExecutorService executorService)
+ public static void finalize(
+ AppView<AppInfo> appView, Timing timing, ExecutorService executorService)
throws ExecutionException {
assert !appView.appInfo().hasClassHierarchy();
assert !appView.appInfo().hasLiveness();
appView.options().testing.checkDeterminism(appView);
- Result result = appView.getSyntheticItems().computeFinalSynthetics(appView);
+ Result result = appView.getSyntheticItems().computeFinalSynthetics(appView, timing);
appView.setAppInfo(new AppInfo(result.commit, result.mainDexInfo));
if (result.lens != null) {
appView.setAppInfo(
@@ -177,11 +182,11 @@
}
public static void finalizeWithClassHierarchy(
- AppView<AppInfoWithClassHierarchy> appView, ExecutorService executorService)
+ AppView<AppInfoWithClassHierarchy> appView, ExecutorService executorService, Timing timing)
throws ExecutionException {
assert !appView.appInfo().hasLiveness();
appView.options().testing.checkDeterminism(appView);
- Result result = appView.getSyntheticItems().computeFinalSynthetics(appView);
+ Result result = appView.getSyntheticItems().computeFinalSynthetics(appView, timing);
appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(result.commit));
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(result.mainDexInfo));
if (result.lens != null) {
@@ -199,10 +204,10 @@
}
public static void finalizeWithLiveness(
- AppView<AppInfoWithLiveness> appView, ExecutorService executorService)
+ AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing)
throws ExecutionException {
appView.options().testing.checkDeterminism(appView);
- Result result = appView.getSyntheticItems().computeFinalSynthetics(appView);
+ Result result = appView.getSyntheticItems().computeFinalSynthetics(appView, timing);
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(result.mainDexInfo));
if (result.lens != null) {
appView.rewriteWithLensAndApplication(result.lens, result.commit.getApplication().asDirect());
@@ -213,7 +218,7 @@
appView.pruneItems(result.prunedItems, executorService);
}
- Result computeFinalSynthetics(AppView<?> appView) {
+ Result computeFinalSynthetics(AppView<?> appView, Timing timing) {
assert verifyNoNestedSynthetics(appView.dexItemFactory());
assert verifyOneSyntheticPerSyntheticClass();
DexApplication application;
@@ -227,9 +232,18 @@
Map<String, NumberGenerator> generators = new HashMap<>();
application =
buildLensAndProgram(
+ timing,
appView,
- computeEquivalences(appView, committed.getMethods(), generators, lensBuilder),
- computeEquivalences(appView, committed.getClasses(), generators, lensBuilder),
+ timing.time(
+ "Method equivalence",
+ () ->
+ computeEquivalences(
+ appView, committed.getMethods(), generators, lensBuilder, timing)),
+ timing.time(
+ "Class equivalence",
+ () ->
+ computeEquivalences(
+ appView, committed.getClasses(), generators, lensBuilder, timing)),
lensBuilder,
(clazz, reference) ->
finalClassesBuilder.put(clazz.getType(), ImmutableList.of(reference)),
@@ -289,13 +303,15 @@
AppView<?> appView,
ImmutableMap<DexType, List<R>> references,
Map<String, NumberGenerator> generators,
- Builder lensBuilder) {
+ Builder lensBuilder,
+ Timing timing) {
boolean intermediate = appView.options().intermediate;
Map<DexType, D> definitions = lookupDefinitions(appView, references);
ClassToFeatureSplitMap classToFeatureSplitMap =
appView.appInfo().hasClassHierarchy()
? appView.appInfo().withClassHierarchy().getClassToFeatureSplitMap()
: ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap();
+ timing.begin("Potential equivalences");
Collection<List<D>> potentialEquivalences =
computePotentialEquivalences(
definitions,
@@ -304,13 +320,15 @@
appView.graphLens(),
classToFeatureSplitMap,
synthetics);
+ timing.end();
return computeActualEquivalences(
potentialEquivalences,
generators,
appView,
intermediate,
classToFeatureSplitMap,
- lensBuilder);
+ lensBuilder,
+ timing);
}
private boolean isNotSyntheticType(DexType type) {
@@ -361,6 +379,7 @@
}
private static DexApplication buildLensAndProgram(
+ Timing timing,
AppView<?> appView,
Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> syntheticMethodGroups,
Map<DexType, EquivalenceGroup<SyntheticProgramClassDefinition>> syntheticClassGroups,
@@ -448,10 +467,12 @@
assert verifyNonRepresentativesRemovedFromApplication(application, syntheticClassGroups);
assert verifyNonRepresentativesRemovedFromApplication(application, syntheticMethodGroups);
+ timing.begin("Tree fixing");
DexApplication.Builder<?> builder = application.builder();
treeFixer.fixupClasses(deduplicatedClasses);
builder.replaceProgramClasses(treeFixer.fixupClasses(application.classes()));
application = builder.build();
+ timing.end();
}
DexString syntheticSourceFileName =
@@ -459,6 +480,7 @@
? appView.dexItemFactory().createString("R8$$SyntheticClass")
: appView.dexItemFactory().createString("D8$$SyntheticClass");
+ timing.begin("Add final synthetics");
// Add the synthesized from after repackaging which changed class definitions.
final DexApplication appForLookup = application;
syntheticClassGroups.forEach(
@@ -491,7 +513,9 @@
representative.getContext(),
syntheticMethodDefinition.getReference()));
});
+ timing.end();
+ timing.begin("Finish lens");
Iterables.<EquivalenceGroup<? extends SyntheticDefinition<?, ?, DexProgramClass>>>concat(
syntheticClassGroups.values(), syntheticMethodGroups.values())
.forEach(
@@ -511,6 +535,7 @@
lensBuilder.setRepresentative(rewrittenMethod, method);
}
}));
+ timing.end();
for (DexType key : syntheticMethodGroups.keySet()) {
assert application.definitionFor(key) != null;
@@ -557,9 +582,11 @@
AppView<?> appView,
boolean intermediate,
ClassToFeatureSplitMap classToFeatureSplitMap,
- Builder lensBuilder) {
+ Builder lensBuilder,
+ Timing timing) {
Map<String, List<EquivalenceGroup<T>>> groupsPerPrefix = new HashMap<>();
Map<DexType, EquivalenceGroup<T>> equivalences = new IdentityHashMap<>();
+ timing.begin("Groups");
potentialEquivalences.forEach(
members -> {
List<EquivalenceGroup<T>> groups =
@@ -581,6 +608,8 @@
}
}
});
+ timing.end();
+ timing.begin("External creation");
groupsPerPrefix.forEach(
(externalSyntheticTypePrefix, groups) -> {
Comparator<EquivalenceGroup<T>> comparator = this::compareForFinalGroupSorting;
@@ -613,6 +642,7 @@
equivalences.put(representativeType, group);
}
});
+ timing.end();
equivalences.forEach(
(representativeType, group) ->
group.forEach(
@@ -639,6 +669,9 @@
List<EquivalenceGroup<T>> groups = new ArrayList<>();
// Each other member is in a shared group if it is actually equivalent to the first member.
for (T synthetic : potentialEquivalence) {
+ if (groups.size() > GROUP_COUNT_THRESHOLD) {
+ return ListUtils.map(potentialEquivalence, m -> new EquivalenceGroup<>(m, true));
+ }
boolean mustBeRepresentative = isPinned(appView, synthetic);
EquivalenceGroup<T> equivalenceGroup = null;
for (EquivalenceGroup<T> group : groups) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 08d9d27..407e91d 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -43,6 +43,7 @@
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
@@ -1086,9 +1087,9 @@
// Finalization of synthetic items.
- Result computeFinalSynthetics(AppView<?> appView) {
+ Result computeFinalSynthetics(AppView<?> appView, Timing timing) {
assert !hasPendingSyntheticClasses();
return new SyntheticFinalization(appView.options(), this, committed)
- .computeFinalSynthetics(appView);
+ .computeFinalSynthetics(appView, timing);
}
}