| // Copyright (c) 2017, 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; |
| |
| import static com.android.tools.r8.benchmarks.BenchmarkUtils.printRuntimeNanoseconds; |
| |
| import com.android.tools.r8.ByteDataView; |
| import com.android.tools.r8.ClassFileResourceProvider; |
| import com.android.tools.r8.CompilationFailedException; |
| import com.android.tools.r8.CompilationMode; |
| import com.android.tools.r8.D8; |
| import com.android.tools.r8.D8Command; |
| import com.android.tools.r8.D8Command.Builder; |
| import com.android.tools.r8.DexFilePerClassFileConsumer; |
| import com.android.tools.r8.DexFilePerClassFileConsumer.ForwardingConsumer; |
| import com.android.tools.r8.DexIndexedConsumer; |
| import com.android.tools.r8.DiagnosticsHandler; |
| import com.android.tools.r8.ProgramConsumer; |
| import com.android.tools.r8.ProgramResource; |
| import com.android.tools.r8.ProgramResource.Kind; |
| import com.android.tools.r8.ResourceException; |
| import com.android.tools.r8.origin.Origin; |
| import com.android.tools.r8.origin.PathOrigin; |
| import com.android.tools.r8.utils.DescriptorUtils; |
| import com.android.tools.r8.utils.ThreadUtils; |
| import com.android.tools.r8.utils.ZipUtils; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.io.ByteStreams; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ExecutorService; |
| |
| public class FrameworkIncrementalDexingBenchmark { |
| private static final int ITERATIONS = 100; |
| private static final int API = 24; |
| private static final Path JAR_DESUGARED = |
| Paths.get("third_party", "framework", "framework_14082017_desugared.jar"); |
| private static final Path JAR_NOT_DESUGARED = |
| Paths.get("third_party", "framework", "framework_14082017.jar"); |
| private static final Path LIB = |
| Paths.get("third_party", "android_jar", "lib-v" + API, "android.jar"); |
| |
| static class InMemoryClassPathProvider implements ClassFileResourceProvider { |
| Origin origin; |
| Map<String, byte[]> resources; |
| |
| InMemoryClassPathProvider(Path archive) throws IOException { |
| origin = new PathOrigin(archive); |
| ImmutableMap.Builder<String, byte[]> builder = ImmutableMap.builder(); |
| ZipUtils.iter( |
| archive.toString(), |
| (entry, stream) -> { |
| String name = entry.getName(); |
| if (ZipUtils.isClassFile(name)) { |
| String descriptor = DescriptorUtils.guessTypeDescriptor(name); |
| builder.put(descriptor, ByteStreams.toByteArray(stream)); |
| } |
| }); |
| resources = builder.build(); |
| } |
| |
| @Override |
| public Set<String> getClassDescriptors() { |
| return resources.keySet(); |
| } |
| |
| @Override |
| public ProgramResource getProgramResource(String descriptor) { |
| byte[] bytes = resources.get(descriptor); |
| return bytes == null |
| ? null |
| : ProgramResource.fromBytes( |
| new EntryOrigin(descriptor, origin), |
| Kind.CF, |
| bytes, |
| Collections.singleton(descriptor)); |
| } |
| } |
| |
| static class EntryOrigin extends Origin { |
| final String descriptor; |
| |
| public EntryOrigin(String descriptor, Origin parent) { |
| super(parent); |
| this.descriptor = descriptor; |
| } |
| |
| @Override |
| public String part() { |
| return descriptor; |
| } |
| } |
| |
| private static String title(String title, boolean desugar) { |
| return "FrameworkIncremental" + (desugar ? title : "NoDesugar" + title); |
| } |
| |
| private static void compileAll( |
| Path input, |
| InMemoryClassPathProvider provider, |
| boolean desugar, |
| Map<String, ProgramResource> outputs, |
| ExecutorService executor) |
| throws IOException, CompilationFailedException { |
| |
| ProgramConsumer consumer = |
| new DexFilePerClassFileConsumer.ForwardingConsumer(null) { |
| @Override |
| public synchronized void accept( |
| String primaryClassDescriptor, |
| ByteDataView data, |
| Set<String> descriptors, |
| DiagnosticsHandler handler) { |
| ProgramResource resource = ProgramResource.fromBytes( |
| Origin.unknown(), Kind.DEX, data.copyByteData(), descriptors); |
| for (String descriptor : descriptors) { |
| assert !outputs.containsKey(descriptor); |
| if (provider.resources.containsKey(descriptor)) { |
| outputs.put(descriptor, resource); |
| } |
| } |
| } |
| }; |
| |
| long start = System.nanoTime(); |
| D8.run( |
| D8Command.builder() |
| .setMinApiLevel(API) |
| .setIntermediate(true) |
| .setMode(CompilationMode.DEBUG) |
| .addProgramFiles(input) |
| .addLibraryFiles(LIB) |
| .setDisableDesugaring(!desugar) |
| .setProgramConsumer(consumer) |
| .build(), |
| executor); |
| printRuntimeNanoseconds(title("DexAll", desugar), System.nanoTime() - start); |
| } |
| |
| private static void compileGroupsOf( |
| int count, |
| List<String> descriptors, |
| InMemoryClassPathProvider provider, |
| boolean desugar, |
| Map<String, ProgramResource> outputs, |
| ExecutorService executor) |
| throws IOException, CompilationFailedException { |
| ProgramConsumer consumer = |
| new ForwardingConsumer(null) { |
| @Override |
| public synchronized void accept( |
| String primaryClassDescriptor, |
| ByteDataView data, |
| Set<String> descriptors, |
| DiagnosticsHandler handler) { |
| ProgramResource resource = ProgramResource.fromBytes( |
| Origin.unknown(), Kind.DEX, data.copyByteData(), descriptors); |
| for (String descriptor : descriptors) { |
| if (provider.resources.containsKey(descriptor)) { |
| outputs.put(descriptor, resource); |
| } |
| } |
| } |
| }; |
| |
| descriptors.sort(String::compareTo); |
| int increment = descriptors.size() / ITERATIONS; |
| long start = System.nanoTime(); |
| for (int iteration = 0; iteration < ITERATIONS; iteration++) { |
| int index = iteration * increment; |
| Builder builder = |
| D8Command.builder() |
| .setMinApiLevel(API) |
| .setIntermediate(true) |
| .setMode(CompilationMode.DEBUG) |
| .addClasspathResourceProvider(provider) |
| .addLibraryFiles(LIB) |
| .setProgramConsumer(consumer) |
| .setDisableDesugaring(!desugar); |
| for (int j = 0; j < count; j++) { |
| builder.addClassProgramData(provider.resources.get(descriptors.get(index + j)), |
| Origin.unknown()); |
| } |
| D8.run(builder.build(), executor); |
| } |
| printRuntimeNanoseconds(title("DexGroupsOf" + count, desugar), System.nanoTime() - start); |
| } |
| |
| private static void merge( |
| boolean desugar, Map<String, ProgramResource> outputs, ExecutorService executor) |
| throws IOException, CompilationFailedException, ResourceException { |
| Builder builder = |
| D8Command.builder() |
| .setMinApiLevel(API) |
| .setIntermediate(false) |
| .setMode(CompilationMode.DEBUG) |
| .setProgramConsumer(DexIndexedConsumer.emptyConsumer()) |
| .setDisableDesugaring(true); |
| for (ProgramResource input : outputs.values()) { |
| try (InputStream inputStream = input.getByteStream()) { |
| builder.addDexProgramData(ByteStreams.toByteArray(inputStream), input.getOrigin()); |
| } |
| } |
| long start = System.nanoTime(); |
| D8.run( |
| builder // never need to desugar when merging dex. |
| .build(), |
| executor); |
| printRuntimeNanoseconds(title("DexMerge", desugar), System.nanoTime() - start); |
| } |
| |
| public static void main(String[] args) |
| throws IOException, CompilationFailedException, ResourceException { |
| boolean desugar = Arrays.asList(args).contains("--desugar"); |
| Path input = desugar ? JAR_NOT_DESUGARED : JAR_DESUGARED; |
| InMemoryClassPathProvider provider = new InMemoryClassPathProvider(input); |
| List<String> descriptors = new ArrayList<>(provider.getClassDescriptors()); |
| Map<String, ProgramResource> outputs = new HashMap<>(provider.getClassDescriptors().size()); |
| int threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2; |
| ExecutorService executor = ThreadUtils.getExecutorService(threads); |
| try { |
| compileAll(input, provider, desugar, outputs, executor); |
| compileGroupsOf(1, descriptors, provider, desugar, outputs, executor); |
| compileGroupsOf(10, descriptors, provider, desugar, outputs, executor); |
| compileGroupsOf(100, descriptors, provider, desugar, outputs, executor); |
| merge(desugar, outputs, executor); |
| // TODO: We should run dex2oat to verify the compilation. |
| } finally { |
| executor.shutdown(); |
| } |
| } |
| } |