blob: 49fd3188956cdf5ea7ec141bc91c2a8ffecfb2d7 [file] [log] [blame]
// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.benchmarks.appdumps;
import com.android.tools.r8.LibraryDesugaringTestConfiguration;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.benchmarks.BenchmarkBase;
import com.android.tools.r8.benchmarks.BenchmarkConfig;
import com.android.tools.r8.benchmarks.BenchmarkConfigError;
import com.android.tools.r8.benchmarks.BenchmarkDependency;
import com.android.tools.r8.benchmarks.BenchmarkEnvironment;
import com.android.tools.r8.benchmarks.BenchmarkMethod;
import com.android.tools.r8.benchmarks.BenchmarkMetric;
import com.android.tools.r8.benchmarks.BenchmarkResultsSingle;
import com.android.tools.r8.benchmarks.BenchmarkSuite;
import com.android.tools.r8.benchmarks.BenchmarkTarget;
import com.android.tools.r8.dump.CompilerDump;
import com.android.tools.r8.dump.DumpOptions;
import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
import com.android.tools.r8.keepanno.annotations.KeepEdge;
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
import com.android.tools.r8.keepanno.annotations.KeepTarget;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.lang.annotation.RetentionPolicy;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class AppDumpBenchmarkBuilder {
@KeepEdge(
consequences =
@KeepTarget(
kind = KeepItemKind.ONLY_METHODS,
methodAnnotatedByClassName = "androidx.compose.runtime.Composable",
constraints = {},
constrainAnnotations =
@AnnotationPattern(
name = "androidx.compose.runtime.Composable",
retention = RetentionPolicy.CLASS)))
static class KeepComposableAnnotations {}
public static AppDumpBenchmarkBuilder builder() {
return new AppDumpBenchmarkBuilder();
}
private String name;
private BenchmarkDependency dumpDependency;
private int fromRevision = -1;
private boolean enableLibraryDesugaring = true;
private final List<String> programPackages = new ArrayList<>();
public void verify() {
if (name == null) {
throw new BenchmarkConfigError("Missing name");
}
if (dumpDependency == null) {
throw new BenchmarkConfigError("Missing dump");
}
if (fromRevision < 0) {
throw new BenchmarkConfigError("Missing from-revision");
}
}
public AppDumpBenchmarkBuilder setEnableLibraryDesugaring(boolean enableLibraryDesugaring) {
this.enableLibraryDesugaring = enableLibraryDesugaring;
return this;
}
public AppDumpBenchmarkBuilder setName(String name) {
this.name = name;
return this;
}
public AppDumpBenchmarkBuilder setDumpDependencyPath(Path dumpDependencyPath) {
return setDumpDependency(
new BenchmarkDependency(
"appdump",
dumpDependencyPath.getFileName().toString(),
dumpDependencyPath.getParent()));
}
public AppDumpBenchmarkBuilder setDumpDependency(BenchmarkDependency dependency) {
this.dumpDependency = dependency;
return this;
}
public AppDumpBenchmarkBuilder setFromRevision(int fromRevision) {
this.fromRevision = fromRevision;
return this;
}
public AppDumpBenchmarkBuilder addProgramPackages(String... pkgs) {
return addProgramPackages(Arrays.asList(pkgs));
}
public AppDumpBenchmarkBuilder addProgramPackages(Collection<String> pkgs) {
programPackages.addAll(pkgs);
return this;
}
public BenchmarkConfig buildR8() {
return buildR8(getDefaultR8Configuration());
}
public BenchmarkConfig buildR8(ThrowableConsumer<? super R8FullTestBuilder> configuration) {
verify();
return BenchmarkConfig.builder()
.setName(name)
.setTarget(BenchmarkTarget.R8)
.setSuite(BenchmarkSuite.OPENSOURCE_BENCHMARKS)
.setMethod(runR8(this, configuration))
.setFromRevision(fromRevision)
.addDependency(dumpDependency)
.measureRunTime()
.measureCodeSize()
.measureInstructionCodeSize()
.measureComposableInstructionCodeSize()
.measureDexSegmentsCodeSize()
.measureDex2OatCodeSize()
.setTimeout(10, TimeUnit.MINUTES)
.build();
}
public BenchmarkConfig buildR8WithResourceShrinking() {
return buildR8WithResourceShrinking(getDefaultR8Configuration());
}
public BenchmarkConfig buildR8WithResourceShrinking(
ThrowableConsumer<? super R8FullTestBuilder> configuration) {
verify();
return BenchmarkConfig.builder()
.setName(name)
.setTarget(BenchmarkTarget.R8)
.setSuite(BenchmarkSuite.OPENSOURCE_BENCHMARKS)
.setMethod(runR8WithResourceShrinking(this, configuration))
.setFromRevision(fromRevision)
.addDependency(dumpDependency)
// TODO(b/368282141): Also measure resource size.
.measureRunTime()
.measureCodeSize()
.measureInstructionCodeSize()
.measureComposableInstructionCodeSize()
.measureDexSegmentsCodeSize()
.measureDex2OatCodeSize()
// TODO(b/373550435): Update dex2oat to enable checking absence of verification errors on
// SystemUI.
.setEnableDex2OatVerification(!name.equals("SystemUIApp"))
.setTimeout(10, TimeUnit.MINUTES)
.build();
}
public BenchmarkConfig buildIncrementalD8() {
verify();
if (programPackages.isEmpty()) {
throw new BenchmarkConfigError(
"Incremental benchmark should specifiy at least one program package");
}
return BenchmarkConfig.builder()
.setName(name)
.setTarget(BenchmarkTarget.D8)
.setSuite(BenchmarkSuite.OPENSOURCE_BENCHMARKS)
.setMethod(runIncrementalD8(this))
.setFromRevision(fromRevision)
.addDependency(dumpDependency)
.addSubBenchmark(nameForDexPart(), BenchmarkMetric.RunTimeRaw)
.addSubBenchmark(nameForMergePart(), BenchmarkMetric.RunTimeRaw)
.setTimeout(10, TimeUnit.MINUTES)
.build();
}
public BenchmarkConfig buildBatchD8() {
verify();
return BenchmarkConfig.builder()
.setName(name)
.setTarget(BenchmarkTarget.D8)
.setSuite(BenchmarkSuite.OPENSOURCE_BENCHMARKS)
.setMethod(runBatchD8(this))
.setFromRevision(fromRevision)
.addDependency(dumpDependency)
.measureRunTime()
.measureCodeSize()
.setTimeout(10, TimeUnit.MINUTES)
.build();
}
private String nameForCodePart() {
return name + "Code";
}
private String nameForDexPart() {
return name + "Dex";
}
private String nameForResourcePart() {
return name + "Resource";
}
private String nameForRuntimePart() {
return name + "Runtime";
}
private String nameForMergePart() {
return name + "Merge";
}
private CompilerDump getExtractedDump(BenchmarkEnvironment environment) throws IOException {
return getExtractedDump(environment, false);
}
private CompilerDump getExtractedDump(
BenchmarkEnvironment environment, boolean enableResourceShrinking) throws IOException {
String dumpName = enableResourceShrinking ? "dump_app_res.zip" : "dump_app.zip";
Path dump = dumpDependency.getRoot(environment).resolve(dumpName);
return CompilerDump.fromArchive(dump, environment.getTemp().newFolder().toPath());
}
private static void addDesugaredLibrary(
TestCompilerBuilder<?, ?, ?, ?, ?> builder, CompilerDump dump) {
Path config = dump.getDesugaredLibraryFile();
if (Files.exists(config)) {
builder.enableCoreLibraryDesugaring(
LibraryDesugaringTestConfiguration.forSpecification(config));
}
}
private static ThrowableConsumer<? super R8FullTestBuilder> getDefaultR8Configuration() {
return testBuilder ->
testBuilder
.allowUnnecessaryDontWarnWildcards()
.allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
// TODO(b/222228826): Disallow unrecognized diagnostics and open interfaces.
.allowDiagnosticMessages()
.addOptionsModification(
options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces());
}
private static ThrowableConsumer<? super R8FullTestBuilder>
getKeepComposableAnnotationsConfiguration() {
return testBuilder ->
testBuilder
.addProgramClasses(KeepComposableAnnotations.class)
.addKeepClassAndMembersRules("androidx.compose.runtime.Composable")
.addKeepRuntimeVisibleAnnotations()
.enableExperimentalKeepAnnotations();
}
private static BenchmarkMethod runR8(
AppDumpBenchmarkBuilder builder, ThrowableConsumer<? super R8FullTestBuilder> configuration) {
return internalRunR8(builder, false, configuration);
}
private static BenchmarkMethod runR8WithResourceShrinking(
AppDumpBenchmarkBuilder builder, ThrowableConsumer<? super R8FullTestBuilder> configuration) {
return internalRunR8(builder, true, configuration);
}
private static BenchmarkMethod internalRunR8(
AppDumpBenchmarkBuilder builder,
boolean enableResourceShrinking,
ThrowableConsumer<? super R8FullTestBuilder> configuration) {
return environment ->
BenchmarkBase.runner(environment)
.setWarmupIterations(1)
.run(
results -> {
CompilerDump dump =
builder.getExtractedDump(environment, enableResourceShrinking);
DumpOptions dumpProperties = dump.getBuildProperties();
TestBase.testForR8(environment.getTemp(), Backend.DEX)
.addProgramFiles(dump.getProgramArchive())
.addLibraryFiles(dump.getLibraryArchive())
.addKeepRuleFiles(dump.getProguardConfigFile())
.addOptionsModification(
options -> {
options.apiModelingOptions().androidApiExtensionPackages =
dumpProperties.getAndroidApiExtensionPackages();
options
.horizontalClassMergerOptions()
.setEnableSameFilePolicy(dumpProperties.getEnableSameFilePolicy());
})
.enableIsolatedSplits(dumpProperties.getIsolatedSplits())
.setMinApi(dumpProperties.getMinApi())
.apply(
testBuilder -> {
dump.forEachFeatureArchive(testBuilder::addFeatureSplit);
addDesugaredLibrary(testBuilder, dump);
})
.apply(configuration)
.applyIf(
environment.getConfig().containsComposableCodeSizeMetric(),
getKeepComposableAnnotationsConfiguration())
.applyIf(
enableResourceShrinking,
b ->
b.enableOptimizedShrinking()
.setAndroidResourcesFromPath(dump.getAndroidResources()))
.apply(
r -> {
try {
// TODO(b/368282141): Also emit resource size.
r.benchmarkCompile(results)
.benchmarkCodeSize(results)
.benchmarkInstructionCodeSize(results)
.benchmarkDexSegmentsCodeSize(results)
.benchmarkDex2OatCodeSize(
results,
environment.getConfig().isDex2OatVerificationEnabled());
} catch (AbortBenchmarkException e) {
// Ignore.
}
});
});
}
private static BenchmarkMethod runBatchD8(AppDumpBenchmarkBuilder builder) {
return environment ->
BenchmarkBase.runner(environment)
.setWarmupIterations(1)
.run(
results -> {
CompilerDump dump = builder.getExtractedDump(environment);
DumpOptions dumpProperties = dump.getBuildProperties();
TestBase.testForD8(environment.getTemp(), Backend.DEX)
.addProgramFiles(dump.getProgramArchive())
.addLibraryFiles(dump.getLibraryArchive())
.setMinApi(dumpProperties.getMinApi())
.applyIf(builder.enableLibraryDesugaring, b -> addDesugaredLibrary(b, dump))
.benchmarkCompile(results)
.benchmarkCodeSize(results);
});
}
private static BenchmarkMethod runIncrementalD8(AppDumpBenchmarkBuilder builder) {
return environment ->
BenchmarkBase.runner(environment)
.setWarmupIterations(1)
.reportResultSum()
.run(
results -> {
CompilerDump dump = builder.getExtractedDump(environment);
DumpOptions dumpProperties = dump.getBuildProperties();
int numShards = 1;
PackageSplitResources resources =
PackageSplitResources.create(
environment.getTemp(),
dump.getProgramArchive(),
builder.programPackages,
numShards);
// Compile all files to a single DEX file.
List<List<Path>> compiledShards = new ArrayList<>();
BenchmarkResultsSingle dexResults =
new BenchmarkResultsSingle(
"tmp", ImmutableSet.of(BenchmarkMetric.RunTimeRaw));
for (List<Path> shard : resources.getShards()) {
List<Path> compiledShard = new ArrayList<>(shard.size());
for (Path programFile : shard) {
TestBase.testForD8(environment.getTemp())
.addProgramFiles(programFile)
.addClasspathFiles(dump.getProgramArchive())
.addLibraryFiles(dump.getLibraryArchive())
.applyIf(
builder.enableLibraryDesugaring, b -> addDesugaredLibrary(b, dump))
.debug()
.setIntermediate(true)
.setMinApi(dumpProperties.getMinApi())
.benchmarkCompile(dexResults)
.writeToZip(compiledShard::add);
}
compiledShards.add(compiledShard);
}
dexResults.doAverage();
results
.getSubResults(builder.nameForDexPart())
.addRuntimeResult(dexResults.getRuntimeResults().getLong(0));
// Merge each compiled shard.
for (List<Path> compiledShard : compiledShards) {
TestBase.testForD8(environment.getTemp())
.addProgramFiles(compiledShard)
.addLibraryFiles(dump.getLibraryArchive())
.debug()
.setMinApi(dumpProperties.getMinApi())
.benchmarkCompile(results.getSubResults(builder.nameForMergePart()));
}
});
}
}