Add NowInAndroid D8 incremental benchmark
This adds support for having benchmarks with sub benchmarks write the results to disk and integrates this with perf.py.
Change-Id: I487e40eafb2d6725486d0adfa2f39677be689dad
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index a183511..4a0c15e 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -18,6 +18,7 @@
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
+import java.util.function.IntFunction;
import java.util.function.Predicate;
public class ListUtils {
@@ -247,6 +248,14 @@
return list;
}
+ public static <T> ArrayList<T> newInitializedArrayList(int size, IntFunction<T> fn) {
+ ArrayList<T> list = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ list.add(fn.apply(i));
+ }
+ return list;
+ }
+
public static <T> ImmutableList<T> newImmutableList(ForEachable<T> forEachable) {
ImmutableList.Builder<T> builder = ImmutableList.builder();
forEachable.forEach(builder::add);
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java
index dc841ba..7d006fc 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java
@@ -6,11 +6,13 @@
import com.android.tools.r8.DexSegments.SegmentInfo;
import com.android.tools.r8.errors.Unimplemented;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
-import java.io.PrintStream;
+import java.io.IOException;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
public class BenchmarkResultsCollection implements BenchmarkResults {
@@ -58,6 +60,11 @@
throw error();
}
+ @Override
+ public void doAverage() {
+ throw new Unimplemented();
+ }
+
private BenchmarkConfigError error() {
throw new BenchmarkConfigError(
"Unexpected attempt to add a result to a the root of a benchmark with sub-benchmarks");
@@ -79,7 +86,11 @@
}
@Override
- public void writeResults(PrintStream out) {
- throw new Unimplemented();
+ public void writeResults(Path path) throws IOException {
+ for (Entry<String, BenchmarkResultsSingle> entry : results.entrySet()) {
+ String name = entry.getKey();
+ BenchmarkResultsSingle result = entry.getValue();
+ result.writeResults(path.resolve(name));
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
index 6a6140a..e029bcd 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.benchmarks;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import com.android.tools.r8.DexSegments.SegmentInfo;
import com.android.tools.r8.dex.DexSection;
import com.google.gson.Gson;
@@ -10,7 +13,10 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
+import java.io.IOException;
import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -116,6 +122,21 @@
}
@Override
+ public void doAverage() {
+ assertFalse(runtimeResults.isEmpty());
+ long averageRuntimeResult =
+ Math.round(runtimeResults.stream().mapToLong(Long::longValue).average().orElse(0));
+ runtimeResults.clear();
+ addRuntimeResult(averageRuntimeResult);
+
+ assertTrue(codeSizeResults.isEmpty());
+ assertTrue(instructionCodeSizeResults.isEmpty());
+ assertTrue(composableInstructionCodeSizeResults.isEmpty());
+ assertTrue(dex2OatSizeResult.isEmpty());
+ assertTrue(dexSegmentsSizeResults.isEmpty());
+ }
+
+ @Override
public BenchmarkResults getSubResults(String name) {
throw new BenchmarkConfigError(
"Unexpected attempt to get sub-results for benchmark without sub-benchmarks");
@@ -247,11 +268,14 @@
}
@Override
- public void writeResults(PrintStream out) {
- Gson gson =
- new GsonBuilder()
- .registerTypeAdapter(BenchmarkResultsSingle.class, new BenchmarkResultsSingleAdapter())
- .create();
- out.print(gson.toJson(this));
+ public void writeResults(Path path) throws IOException {
+ try (PrintStream out = new PrintStream(Files.newOutputStream(path))) {
+ Gson gson =
+ new GsonBuilder()
+ .registerTypeAdapter(
+ BenchmarkResultsSingle.class, new BenchmarkResultsSingleAdapter())
+ .create();
+ out.print(gson.toJson(this));
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java
index 4f16cc4..16084c1 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java
@@ -3,13 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.benchmarks;
+import static org.junit.Assert.assertFalse;
+
import com.android.tools.r8.DexSegments.SegmentInfo;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
-import java.io.PrintStream;
+import java.nio.file.Path;
public class BenchmarkResultsWarmup implements BenchmarkResults {
@@ -74,6 +76,15 @@
}
@Override
+ public void doAverage() {
+ assertFalse(runtimeResults.isEmpty());
+ long averageRuntimeResult =
+ Math.round(runtimeResults.stream().mapToLong(Long::longValue).average().orElse(0));
+ runtimeResults.clear();
+ addRuntimeResult(averageRuntimeResult);
+ }
+
+ @Override
public BenchmarkResults getSubResults(String name) {
// When running warmups all results are amended to the single warmup result.
return this;
@@ -97,7 +108,7 @@
}
@Override
- public void writeResults(PrintStream out) {
+ public void writeResults(Path path) {
throw new Unimplemented();
}
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkRunner.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkRunner.java
index 78441e3..3f1db19 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkRunner.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkRunner.java
@@ -4,9 +4,6 @@
package com.android.tools.r8.benchmarks;
import com.android.tools.r8.benchmarks.BenchmarkResults.ResultMode;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.nio.file.Files;
public class BenchmarkRunner {
@@ -87,7 +84,7 @@
printMetaInfo("benchmark", getBenchmarkIterations(), benchmarkTotalTime);
results.printResults(resultMode, environment.failOnCodeSizeDifferences());
if (environment.hasOutputPath()) {
- writeResults(results);
+ results.writeResults(environment.getOutputPath());
}
System.out.println();
}
@@ -97,11 +94,4 @@
System.out.println(" " + kind + " iterations: " + iterations);
System.out.println(" " + kind + " total time: " + BenchmarkResults.prettyTime(totalTime));
}
-
- private void writeResults(BenchmarkResults results) throws IOException {
- try (PrintStream printStream =
- new PrintStream(Files.newOutputStream(environment.getOutputPath()))) {
- results.writeResults(printStream);
- }
- }
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
index 3f7d657..8383488 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
@@ -161,8 +161,7 @@
.setMethod(runIncrementalD8(this))
.setFromRevision(fromRevision)
.addDependency(dumpDependency)
- .addSubBenchmark(nameForLibraryPart(), BenchmarkMetric.RunTimeRaw)
- .addSubBenchmark(nameForProgramPart(), BenchmarkMetric.RunTimeRaw)
+ .addSubBenchmark(nameForDexPart(), BenchmarkMetric.RunTimeRaw)
.addSubBenchmark(nameForMergePart(), BenchmarkMetric.RunTimeRaw)
.setTimeout(10, TimeUnit.MINUTES)
.build();
@@ -187,16 +186,8 @@
return name + "Code";
}
- private String nameForComposableCodePart() {
- return name + "ComposableCode";
- }
-
- private String nameForLibraryPart() {
- return name + "Library";
- }
-
- private String nameForProgramPart() {
- return name + "Program";
+ private String nameForDexPart() {
+ return name + "Dex";
}
private String nameForResourcePart() {
@@ -348,38 +339,45 @@
results -> {
CompilerDump dump = builder.getExtractedDump(environment);
DumpOptions dumpProperties = dump.getBuildProperties();
+
+ int numShards = 1;
PackageSplitResources resources =
PackageSplitResources.create(
- environment.getTemp(), dump.getProgramArchive(), builder.programPackages);
- if (resources.getPackageFiles().isEmpty()) {
- throw new RuntimeException("Unexpected empty set of program package files");
+ environment.getTemp(),
+ dump.getProgramArchive(),
+ builder.programPackages,
+ numShards);
+
+ // Compile all files to a single DEX file.
+ List<List<Path>> compiledShards = new ArrayList<>();
+ 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(results.getSubResults(builder.nameForDexPart()))
+ .writeToZip(compiledShard::add);
+ }
+ compiledShards.add(compiledShard);
}
+ results.getSubResults(builder.nameForDexPart()).doAverage();
- TestBase.testForD8(environment.getTemp(), Backend.DEX)
- .addProgramFiles(resources.getOtherFiles())
- .addLibraryFiles(dump.getLibraryArchive())
- .setMinApi(dumpProperties.getMinApi())
- .benchmarkCompile(results.getSubResults(builder.nameForLibraryPart()));
-
- List<Path> programOutputs = new ArrayList<>();
- for (Path programFile : resources.getPackageFiles()) {
- programOutputs.add(
- TestBase.testForD8(environment.getTemp(), Backend.DEX)
- .addProgramFiles(programFile)
- .addClasspathFiles(dump.getProgramArchive())
- .addLibraryFiles(dump.getLibraryArchive())
- .setMinApi(dumpProperties.getMinApi())
- .apply(b -> addDesugaredLibrary(b, dump))
- .setIntermediate(true)
- .benchmarkCompile(results.getSubResults(builder.nameForProgramPart()))
- .writeToZip());
+ // 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()));
}
-
- TestBase.testForD8(environment.getTemp(), Backend.DEX)
- .addProgramFiles(programOutputs)
- .addLibraryFiles(dump.getLibraryArchive())
- .setMinApi(dumpProperties.getMinApi())
- .benchmarkCompile(results.getSubResults(builder.nameForMergePart()));
});
}
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/NowInAndroidBenchmarks.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/NowInAndroidBenchmarks.java
index e635966..e0ce0b3 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/appdumps/NowInAndroidBenchmarks.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/NowInAndroidBenchmarks.java
@@ -44,6 +44,12 @@
.setFromRevision(16017)
.buildBatchD8(),
AppDumpBenchmarkBuilder.builder()
+ .setName("NowInAndroidAppIncremental")
+ .setDumpDependencyPath(dump)
+ .setFromRevision(16017)
+ .addProgramPackages("com/google/samples/apps/nowinandroid")
+ .buildIncrementalD8(),
+ AppDumpBenchmarkBuilder.builder()
.setName("NowInAndroidApp")
.setDumpDependencyPath(dump)
.setFromRevision(16017)
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/PackageSplitResources.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/PackageSplitResources.java
index b0566ac..d6dc468 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/appdumps/PackageSplitResources.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/PackageSplitResources.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.benchmarks.appdumps;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ZipUtils;
import java.io.File;
import java.io.IOException;
@@ -15,34 +16,33 @@
public class PackageSplitResources {
- private final List<Path> packageFiles;
- private final List<Path> otherFiles;
+ private final List<List<Path>> shards;
- public PackageSplitResources(List<Path> packageFiles, List<Path> otherFiles) {
- this.packageFiles = packageFiles;
- this.otherFiles = otherFiles;
+ public PackageSplitResources(List<List<Path>> shards) {
+ this.shards = shards;
}
public static PackageSplitResources create(
- TemporaryFolder temp, Path archive, List<String> packagePrefixes) throws IOException {
+ TemporaryFolder temp, Path archive, List<String> packagePrefixes, int numShards)
+ throws IOException {
Path unzipDir = temp.newFolder().toPath();
ZipUtils.unzip(archive, unzipDir);
- List<Path> packageFiles = new ArrayList<>();
- List<Path> otherFiles = new ArrayList<>();
+ List<List<Path>> shards = ListUtils.newInitializedArrayList(numShards, i -> new ArrayList<>());
Files.walk(unzipDir)
.forEachOrdered(
file -> {
if (FileUtils.isClassFile(file)) {
Path relative = unzipDir.relativize(file);
if (isInPackagePrefixes(relative, packagePrefixes)) {
- packageFiles.add(file);
- } else {
- otherFiles.add(file);
+ String packageDir =
+ relative.getParent() != null ? relative.getParent().toString() : "";
+ int shard = Math.abs(packageDir.hashCode() % numShards);
+ shards.get(shard).add(file);
}
}
});
- return new PackageSplitResources(packageFiles, otherFiles);
+ return new PackageSplitResources(shards);
}
private static boolean isInPackagePrefixes(Path file, List<String> programPackages) {
@@ -58,11 +58,7 @@
return false;
}
- public List<Path> getPackageFiles() {
- return packageFiles;
- }
-
- public List<Path> getOtherFiles() {
- return otherFiles;
+ public List<List<Path>> getShards() {
+ return shards;
}
}
diff --git a/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java b/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
index 6f8d5b6..42fb589 100644
--- a/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
+++ b/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
@@ -6,7 +6,8 @@
import com.android.tools.r8.DexSegments.SegmentInfo;
import com.android.tools.r8.utils.StringUtils;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
-import java.io.PrintStream;
+import java.io.IOException;
+import java.nio.file.Path;
public interface BenchmarkResults {
@@ -27,6 +28,8 @@
// Append a resource size result. This is always assumed to be identical if called multiple times.
void addResourceSizeResult(long result);
+ void doAverage();
+
// Get the results collection for a "sub-benchmark" when defining a group of benchmarks.
// This will throw if called on a benchmark without sub-benchmarks.
BenchmarkResults getSubResults(String name);
@@ -37,7 +40,7 @@
void printResults(ResultMode resultMode, boolean failOnCodeSizeDifferences);
- void writeResults(PrintStream out);
+ void writeResults(Path path) throws IOException;
static String prettyTime(long nanoTime) {
return "" + (nanoTime / 1000000) + " ms";
diff --git a/tools/perf.py b/tools/perf.py
index fec3449..ebf6d49 100755
--- a/tools/perf.py
+++ b/tools/perf.py
@@ -55,6 +55,12 @@
'NowInAndroidAppNoJ$': {
'targets': ['d8']
},
+ 'NowInAndroidAppIncremental': {
+ 'targets': ['d8'],
+ 'subBenchmarks': {
+ 'd8': ['Dex', 'Merge']
+ }
+ },
'OwlApp': {
'targets': ['r8-full']
},
@@ -177,6 +183,17 @@
return f'gs://{bucket}/{filename}'
+def ArchiveBenchmarkResult(benchmark, target, benchmark_result_json_files,
+ options, temp):
+ result_file = os.path.join(temp, 'result_file')
+ with open(result_file, 'w') as f:
+ json.dump(MergeBenchmarkResultJsonFiles(benchmark_result_json_files), f)
+ ArchiveOutputFile(result_file,
+ GetArtifactLocation(benchmark, target, options.version,
+ 'result.json'),
+ outdir=options.outdir)
+
+
def ArchiveOutputFile(file, dest, bucket=BUCKET, header=None, outdir=None):
if outdir:
dest_in_outdir = os.path.join(outdir, dest)
@@ -205,10 +222,15 @@
r8jar = compiledump.download_distribution(options.version,
download_options, temp)
for benchmark in options.benchmarks:
+ benchmark_info = BENCHMARKS[benchmark]
targets = [options.target
- ] if options.target else BENCHMARKS[benchmark]['targets']
+ ] if options.target else benchmark_info['targets']
for target in targets:
+ sub_benchmarks = benchmark_info.get('subBenchmarks', {})
+ sub_benchmarks_for_target = sub_benchmarks.get(target, [])
+
if options.skip_if_output_exists:
+ assert len(sub_benchmarks_for_target) == 0, 'Unimplemented'
if options.outdir:
raise NotImplementedError
output = GetGSLocation(
@@ -219,23 +241,41 @@
continue
# Run benchmark.
- benchmark_result_json_files = []
+ if sub_benchmarks_for_target:
+ benchmark_result_json_files = {}
+ for sub_benchmark in sub_benchmarks_for_target:
+ benchmark_result_json_files[sub_benchmark] = []
+ else:
+ benchmark_result_json_files = []
failed = False
for i in range(options.iterations):
utils.Print(
f'Benchmarking {benchmark} ({i+1}/{options.iterations})',
quiet=options.quiet)
- benchhmark_result_file = os.path.join(
- temp, f'result_file_{i}')
+ if sub_benchmarks_for_target:
+ benchmark_result_file = os.path.join(
+ temp, f'result_{i}')
+ os.makedirs(benchmark_result_file)
+ else:
+ benchmark_result_file = os.path.join(
+ temp, f'result_file_{i}')
iteration_cmd = GetRunCmd(benchmark, target, options, [
'--iterations',
str(options.iterations_inner), '--output',
- benchhmark_result_file, '--no-build'
+ benchmark_result_file, '--no-build'
])
try:
subprocess.check_call(iteration_cmd)
- benchmark_result_json_files.append(
- benchhmark_result_file)
+ if sub_benchmarks_for_target:
+ for sub_benchmark in sub_benchmarks_for_target:
+ sub_benchmark_result_file = os.path.join(
+ benchmark_result_file, sub_benchmark)
+ benchmark_result_json_files[
+ sub_benchmark].append(
+ sub_benchmark_result_file)
+ else:
+ benchmark_result_json_files.append(
+ benchmark_result_file)
except subprocess.CalledProcessError as e:
failed = True
any_failed = True
@@ -245,16 +285,16 @@
continue
# Merge results and write output.
- result_file = os.path.join(temp, 'result_file')
- with open(result_file, 'w') as f:
- json.dump(
- MergeBenchmarkResultJsonFiles(
- benchmark_result_json_files), f)
- ArchiveOutputFile(result_file,
- GetArtifactLocation(benchmark, target,
- options.version,
- 'result.json'),
- outdir=options.outdir)
+ if sub_benchmarks_for_target:
+ for sub_benchmark in sub_benchmarks:
+ ArchiveBenchmarkResult(
+ benchmark + sub_benchmark, target,
+ benchmark_result_json_files[sub_benchmark], options,
+ temp)
+ else:
+ ArchiveBenchmarkResult(benchmark, target,
+ benchmark_result_json_files, options,
+ temp)
# Write metadata.
if utils.is_bot():
diff --git a/tools/upload_benchmark_data_to_google_storage.py b/tools/upload_benchmark_data_to_google_storage.py
index f979010..8c47e8a 100755
--- a/tools/upload_benchmark_data_to_google_storage.py
+++ b/tools/upload_benchmark_data_to_google_storage.py
@@ -47,6 +47,19 @@
target, benchmarks):
if not target in benchmark_info['targets']:
return
+ sub_benchmarks = benchmark_info.get('subBenchmarks', {})
+ sub_benchmarks_for_target = sub_benchmarks.get(target, [])
+ if sub_benchmarks_for_target:
+ for sub_benchmark in sub_benchmarks_for_target:
+ RecordSingleBenchmarkResult(commit, benchmark + sub_benchmark,
+ local_bucket, target, benchmarks)
+ else:
+ RecordSingleBenchmarkResult(commit, benchmark, local_bucket, target,
+ benchmarks)
+
+
+def RecordSingleBenchmarkResult(commit, benchmark, local_bucket, target,
+ benchmarks):
filename = perf.GetArtifactLocation(benchmark, target, commit.hash(),
'result.json')
benchmark_data = ParseJsonFromCloudStorage(filename, local_bucket)