aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2016-05-13 18:51:27 +0000
committerGravatar Kristina Chodorow <kchodorow@google.com>2016-05-16 15:16:41 +0000
commita56c1f4a1b47131b6d366a658f1f1e5abe0cdf3d (patch)
tree03b81997aaab9400106f7c2efacf69047425973d /src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java
parente255ce9efe565d83249f71a5ef85a8fc01ef7c10 (diff)
Speed Optimizations:
* Adds threading to the AndroidDataWriter and AndroidDataSerializer. * Changes to a BufferedWriter for the Writer (turns out it's faster for string writing.) * Added buffers to the serializer reading. -- MOS_MIGRATED_REVID=122280993
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java116
1 files changed, 82 insertions, 34 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java b/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java
index c9a11bbe36..3c2cde8666 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java
@@ -18,6 +18,10 @@ import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.devtools.build.android.ParsedAndroidData.Builder;
import com.google.devtools.build.android.ParsedAndroidData.ParsedAndroidDataBuildingPathWalker;
import com.android.ide.common.res2.MergingException;
@@ -34,6 +38,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
@@ -44,6 +50,49 @@ public class AndroidDataMerger {
private static final Logger logger = Logger.getLogger(AndroidDataMerger.class.getCanonicalName());
+ private final class ParseDependencyDataTask implements Callable<Boolean> {
+
+ private final AndroidDataSerializer serializer;
+
+ private final DependencyAndroidData dependency;
+
+ private final Builder targetBuilder;
+
+ private ParseDependencyDataTask(
+ AndroidDataSerializer serializer, DependencyAndroidData dependency, Builder targetBuilder) {
+ this.serializer = serializer;
+ this.dependency = dependency;
+ this.targetBuilder = targetBuilder;
+ }
+
+ @Override
+ public Boolean call() throws Exception {
+ final Builder parsedDataBuilder = ParsedAndroidData.Builder.newBuilder();
+ try {
+ dependency.deserialize(serializer, parsedDataBuilder.consumers());
+ } catch (DeserializationException e) {
+ if (!e.isLegacy()) {
+ throw new MergingException(e);
+ }
+ //TODO(corysmith): List the offending target here.
+ logger.warning(
+ String.format(
+ "\u001B[31mDEPRECATION:\u001B[0m Legacy resources used for %s",
+ dependency.getManifest()));
+ // Legacy android resources -- treat them as direct dependencies.
+ dependency.walk(ParsedAndroidDataBuildingPathWalker.create(parsedDataBuilder));
+ }
+ // The builder isn't threadsafe, so synchronize the copyTo call.
+ synchronized (targetBuilder) {
+ // All the resources are sorted before writing, so they can be aggregated in
+ // whatever order here.
+ parsedDataBuilder.copyTo(targetBuilder);
+ }
+ // Had to return something?
+ return Boolean.TRUE;
+ }
+ }
+
/** Interface for comparing paths. */
interface SourceChecker {
boolean checkEquality(Path one, Path two) throws IOException;
@@ -109,49 +158,69 @@ public class AndroidDataMerger {
}
private final SourceChecker deDuplicator;
+ private final ListeningExecutorService executorService;
- /** Creates a merger with no path deduplication. */
- public static AndroidDataMerger create() {
- return new AndroidDataMerger(NoopSourceChecker.create());
+ /** Creates a merger with no path deduplication and a default {@link ExecutorService}. */
+ public static AndroidDataMerger createWithDefaults() {
+ return createWithDefaultThreadPool(NoopSourceChecker.create());
}
- /** Creates a merger with a custom deduplicator. */
- public static AndroidDataMerger create(SourceChecker deDuplicator) {
- return new AndroidDataMerger(deDuplicator);
+ /** Creates a merger with a custom deduplicator and a default {@link ExecutorService}. */
+ public static AndroidDataMerger createWithDefaultThreadPool(SourceChecker deDuplicator) {
+ return new AndroidDataMerger(deDuplicator,
+ MoreExecutors.newDirectExecutorService());
+ }
+
+ /** Creates a merger with a custom deduplicator and an {@link ExecutorService}. */
+ public static AndroidDataMerger create(
+ SourceChecker deDuplicator, ListeningExecutorService executorService) {
+ return new AndroidDataMerger(deDuplicator, executorService);
}
/** Creates a merger with a file contents hashing deduplicator. */
- public static AndroidDataMerger createWithPathDeduplictor() {
- return create(ContentComparingChecker.create());
+ public static AndroidDataMerger createWithPathDeduplictor(
+ ListeningExecutorService executorService) {
+ return create(ContentComparingChecker.create(), executorService);
}
- private AndroidDataMerger(SourceChecker deDuplicator) {
+ private AndroidDataMerger(SourceChecker deDuplicator, ListeningExecutorService executorService) {
this.deDuplicator = deDuplicator;
+ this.executorService = executorService;
}
/**
* Merges a list of {@link DependencyAndroidData} with a {@link UnvalidatedAndroidData}.
*
* @see AndroidDataMerger#merge(ParsedAndroidData, ParsedAndroidData, UnvalidatedAndroidData,
- * boolean) for details.
+ * boolean) for details.
*/
UnwrittenMergedAndroidData merge(
List<DependencyAndroidData> transitive,
List<DependencyAndroidData> direct,
UnvalidatedAndroidData primary,
boolean allowPrimaryOverrideAll)
- throws IOException, MergingException {
+ throws MergingException {
Stopwatch timer = Stopwatch.createStarted();
try {
final ParsedAndroidData.Builder directBuilder = ParsedAndroidData.Builder.newBuilder();
final ParsedAndroidData.Builder transitiveBuilder = ParsedAndroidData.Builder.newBuilder();
final AndroidDataSerializer serializer = AndroidDataSerializer.create();
+ final List<ListenableFuture<Boolean>> tasks = new ArrayList<>();
for (final DependencyAndroidData dependency : direct) {
- parseDependencyData(directBuilder, serializer, dependency);
+ tasks.add(
+ executorService.submit(
+ new ParseDependencyDataTask(serializer, dependency, directBuilder)));
}
for (final DependencyAndroidData dependency : transitive) {
- parseDependencyData(transitiveBuilder, serializer, dependency);
+ tasks.add(
+ executorService.submit(
+ new ParseDependencyDataTask(serializer, dependency, transitiveBuilder)));
}
+ // Wait for all the parsing to complete.
+ FailedFutureAggregator<MergingException> aggregator =
+ FailedFutureAggregator.createForMergingExceptionWithMessage(
+ "Failure(s) during dependency parsing");
+ aggregator.aggregateAndMaybeThrow(tasks);
logger.fine(
String.format("Merged dependencies read in %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
timer.reset().start();
@@ -162,27 +231,6 @@ public class AndroidDataMerger {
}
}
- private void parseDependencyData(
- final ParsedAndroidData.Builder parsedDataBuilder,
- final AndroidDataSerializer serializer,
- final DependencyAndroidData dependency)
- throws IOException, MergingException {
- try {
- dependency.deserialize(serializer, parsedDataBuilder.consumers());
- } catch (DeserializationException e) {
- if (!e.isLegacy()) {
- throw new MergingException(e);
- }
- //TODO(corysmith): List the offending a target here.
- logger.warning(
- String.format(
- "\u001B[31mDEPRECATION:\u001B[0m Legacy resources used for %s",
- dependency.getManifest()));
- // Legacy android resources -- treat them as direct dependencies.
- dependency.walk(ParsedAndroidDataBuildingPathWalker.create(parsedDataBuilder));
- }
- }
-
/**
* Merges DataResources into an UnwrittenMergedAndroidData.
* <p>