blob: 5ad164c5658bc6c28f6ce35e8271bce70e5d5141 [file] [log] [blame]
Nathan Harmata593dc522016-09-28 23:35:46 +00001// Copyright 2016 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.query2;
15
Googler2b503882016-11-28 21:54:43 +000016import com.google.common.annotations.VisibleForTesting;
nharmata398e6dab2018-04-12 15:31:26 -070017import com.google.common.base.Function;
18import com.google.common.base.Predicate;
19import com.google.common.base.Predicates;
nharmata398e6dab2018-04-12 15:31:26 -070020import com.google.common.collect.ImmutableSet;
Nathan Harmata593dc522016-09-28 23:35:46 +000021import com.google.common.collect.Iterables;
Nathan Harmataf44211c2016-10-10 16:31:18 +000022import com.google.devtools.build.lib.cmdline.PackageIdentifier;
Nathan Harmata41b54172016-11-10 18:54:09 +000023import com.google.devtools.build.lib.concurrent.MultisetSemaphore;
nharmata398e6dab2018-04-12 15:31:26 -070024import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
Nathan Harmata593dc522016-09-28 23:35:46 +000025import com.google.devtools.build.lib.packages.Target;
26import com.google.devtools.build.lib.query2.engine.Callback;
Nathan Harmata7a5a2362017-03-08 22:42:01 +000027import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryTaskFuture;
nharmata398e6dab2018-04-12 15:31:26 -070028import com.google.devtools.build.lib.query2.engine.QueryEnvironment.ThreadSafeMutableSet;
Nathan Harmata593dc522016-09-28 23:35:46 +000029import com.google.devtools.build.lib.query2.engine.QueryException;
30import com.google.devtools.build.lib.query2.engine.QueryExpression;
shreyax159a6112018-06-12 15:34:09 -070031import com.google.devtools.build.lib.query2.engine.QueryExpressionContext;
nharmata398e6dab2018-04-12 15:31:26 -070032import com.google.devtools.build.lib.query2.engine.QueryUtil;
33import com.google.devtools.build.lib.query2.engine.QueryUtil.AggregateAllCallback;
Nathan Harmata7a5a2362017-03-08 22:42:01 +000034import com.google.devtools.build.lib.query2.engine.Uniquifier;
Nathan Harmata593dc522016-09-28 23:35:46 +000035import com.google.devtools.build.lib.vfs.PathFragment;
36import com.google.devtools.build.skyframe.SkyKey;
37import java.util.Collection;
nharmata398e6dab2018-04-12 15:31:26 -070038import java.util.Collections;
nharmata2399df02018-04-10 12:30:03 -070039import java.util.Objects;
Nathan Harmata593dc522016-09-28 23:35:46 +000040import java.util.Set;
Googler45b59532018-05-03 11:10:20 -070041import java.util.concurrent.ConcurrentHashMap;
nharmata2399df02018-04-10 12:30:03 -070042import javax.annotation.Nullable;
Nathan Harmata593dc522016-09-28 23:35:46 +000043
Nathan Harmata593dc522016-09-28 23:35:46 +000044/**
45 * Parallel implementations of various functionality in {@link SkyQueryEnvironment}.
46 *
47 * <p>Special attention is given to memory usage. Naive parallel implementations of query
48 * functionality would lead to memory blowup. Instead of dealing with {@link Target}s, we try to
49 * deal with {@link SkyKey}s as much as possible to reduce the number of {@link Package}s forcibly
50 * in memory at any given time.
51 */
52// TODO(bazel-team): Be more deliberate about bounding memory usage here.
nharmata2399df02018-04-10 12:30:03 -070053public class ParallelSkyQueryUtils {
Googler2b503882016-11-28 21:54:43 +000054
55 /** The maximum number of keys to visit at once. */
56 @VisibleForTesting static final int VISIT_BATCH_SIZE = 10000;
57
Nathan Harmata593dc522016-09-28 23:35:46 +000058 private ParallelSkyQueryUtils() {
59 }
60
Nathan Harmata7a5a2362017-03-08 22:42:01 +000061 static QueryTaskFuture<Void> getAllRdepsUnboundedParallel(
Nathan Harmata593dc522016-09-28 23:35:46 +000062 SkyQueryEnvironment env,
63 QueryExpression expression,
shreyax159a6112018-06-12 15:34:09 -070064 QueryExpressionContext<Target> context,
Nathan Harmata7a5a2362017-03-08 22:42:01 +000065 Callback<Target> callback,
66 MultisetSemaphore<PackageIdentifier> packageSemaphore) {
67 return env.eval(
Nathan Harmata593dc522016-09-28 23:35:46 +000068 expression,
69 context,
Googler7184b6f2017-05-16 05:25:49 +020070 ParallelVisitor.createParallelVisitorCallback(
nharmata398e6dab2018-04-12 15:31:26 -070071 new RdepsUnboundedVisitor.Factory(
72 env,
nharmatab6bf51d2018-07-27 13:49:45 -070073 /*unfilteredUniverse=*/ Predicates.alwaysTrue(),
nharmata398e6dab2018-04-12 15:31:26 -070074 callback,
75 packageSemaphore)));
76 }
77
78 static QueryTaskFuture<Void> getAllRdepsBoundedParallel(
79 SkyQueryEnvironment env,
80 QueryExpression expression,
81 int depth,
shreyax159a6112018-06-12 15:34:09 -070082 QueryExpressionContext<Target> context,
nharmata398e6dab2018-04-12 15:31:26 -070083 Callback<Target> callback,
84 MultisetSemaphore<PackageIdentifier> packageSemaphore) {
85 return env.eval(
86 expression,
87 context,
88 ParallelVisitor.createParallelVisitorCallback(
89 new RdepsBoundedVisitor.Factory(
90 env,
91 depth,
92 /*universe=*/ Predicates.alwaysTrue(),
93 callback,
94 packageSemaphore)));
95 }
96
97 static QueryTaskFuture<Void> getRdepsInUniverseUnboundedParallel(
98 SkyQueryEnvironment env,
99 QueryExpression expression,
nharmatab6bf51d2018-07-27 13:49:45 -0700100 Predicate<SkyKey> unfilteredUniverse,
shreyax159a6112018-06-12 15:34:09 -0700101 QueryExpressionContext<Target> context,
nharmata398e6dab2018-04-12 15:31:26 -0700102 Callback<Target> callback,
103 MultisetSemaphore<PackageIdentifier> packageSemaphore) {
104 return env.eval(
105 expression,
106 context,
107 ParallelVisitor.createParallelVisitorCallback(
nharmatab6bf51d2018-07-27 13:49:45 -0700108 new RdepsUnboundedVisitor.Factory(
109 env, unfilteredUniverse, callback, packageSemaphore)));
nharmata398e6dab2018-04-12 15:31:26 -0700110 }
111
112 static QueryTaskFuture<Predicate<SkyKey>> getDTCSkyKeyPredicateFuture(
113 SkyQueryEnvironment env,
114 QueryExpression expression,
shreyax159a6112018-06-12 15:34:09 -0700115 QueryExpressionContext<Target> context,
nharmata398e6dab2018-04-12 15:31:26 -0700116 int processResultsBatchSize,
117 int concurrencyLevel) {
118 QueryTaskFuture<ThreadSafeMutableSet<Target>> universeValueFuture =
119 QueryUtil.evalAll(env, context, expression);
120
121 Function<ThreadSafeMutableSet<Target>, QueryTaskFuture<Predicate<SkyKey>>>
122 getTransitiveClosureAsyncFunction =
123 universeValue -> {
124 ThreadSafeAggregateAllSkyKeysCallback aggregateAllCallback =
125 new ThreadSafeAggregateAllSkyKeysCallback(concurrencyLevel);
126 return env.executeAsync(
127 () -> {
128 Callback<Target> visitorCallback =
129 ParallelVisitor.createParallelVisitorCallback(
nharmatab6bf51d2018-07-27 13:49:45 -0700130 new UnfilteredTransitiveTraversalValueDTCVisitor.Factory(
nharmata398e6dab2018-04-12 15:31:26 -0700131 env,
132 env.createSkyKeyUniquifier(),
133 processResultsBatchSize,
134 aggregateAllCallback));
135 visitorCallback.process(universeValue);
136 return Predicates.in(aggregateAllCallback.getResult());
137 });
138 };
139
140 return env.transformAsync(universeValueFuture, getTransitiveClosureAsyncFunction);
141 }
142
143 static QueryTaskFuture<Void> getRdepsInUniverseBoundedParallel(
144 SkyQueryEnvironment env,
145 QueryExpression expression,
146 int depth,
147 Predicate<SkyKey> universe,
shreyax159a6112018-06-12 15:34:09 -0700148 QueryExpressionContext<Target> context,
nharmata398e6dab2018-04-12 15:31:26 -0700149 Callback<Target> callback,
150 MultisetSemaphore<PackageIdentifier> packageSemaphore) {
151 return env.eval(
152 expression,
153 context,
154 ParallelVisitor.createParallelVisitorCallback(
155 new RdepsBoundedVisitor.Factory(
156 env,
157 depth,
158 universe,
159 callback,
160 packageSemaphore)));
Nathan Harmata593dc522016-09-28 23:35:46 +0000161 }
162
163 /** Specialized parallel variant of {@link SkyQueryEnvironment#getRBuildFiles}. */
164 static void getRBuildFilesParallel(
165 SkyQueryEnvironment env,
166 Collection<PathFragment> fileIdentifiers,
nharmata1bd4aaf2017-10-31 11:23:04 -0400167 Callback<Target> callback) throws QueryException, InterruptedException {
Nathan Harmata7a5a2362017-03-08 22:42:01 +0000168 Uniquifier<SkyKey> keyUniquifier = env.createSkyKeyUniquifier();
Nathan Harmata41b54172016-11-10 18:54:09 +0000169 RBuildFilesVisitor visitor =
nharmata1bd4aaf2017-10-31 11:23:04 -0400170 new RBuildFilesVisitor(env, keyUniquifier, callback);
nharmatafa9b01e2017-11-27 08:16:38 -0800171 visitor.visitAndWaitForCompletion(env.getFileStateKeysForFileFragments(fileIdentifiers));
Nathan Harmata593dc522016-09-28 23:35:46 +0000172 }
173
shreyax2643d4b2018-05-25 11:12:11 -0700174 static QueryTaskFuture<Void> getDepsUnboundedParallel(
175 SkyQueryEnvironment env,
176 QueryExpression expression,
shreyax159a6112018-06-12 15:34:09 -0700177 QueryExpressionContext<Target> context,
shreyax2643d4b2018-05-25 11:12:11 -0700178 Callback<Target> callback,
179 MultisetSemaphore<PackageIdentifier> packageSemaphore,
180 boolean depsNeedFiltering,
181 Callback<Target> errorReporter) {
182 return env.eval(
183 expression,
184 context,
185 ParallelVisitor.createParallelVisitorCallback(
186 new DepsUnboundedVisitor.Factory(
shreyax159a6112018-06-12 15:34:09 -0700187 env, callback, packageSemaphore, depsNeedFiltering, errorReporter, context)));
shreyax2643d4b2018-05-25 11:12:11 -0700188 }
189
shreyax19b43772018-05-18 11:45:44 -0700190 static class DepAndRdep {
191 @Nullable final SkyKey dep;
192 final SkyKey rdep;
nharmata2399df02018-04-10 12:30:03 -0700193
shreyax19b43772018-05-18 11:45:44 -0700194 DepAndRdep(@Nullable SkyKey dep, SkyKey rdep) {
nharmata2399df02018-04-10 12:30:03 -0700195 this.dep = dep;
196 this.rdep = rdep;
197 }
198
199 @Override
200 public boolean equals(Object obj) {
201 if (!(obj instanceof DepAndRdep)) {
202 return false;
203 }
204 DepAndRdep other = (DepAndRdep) obj;
205 return Objects.equals(dep, other.dep) && rdep.equals(other.rdep);
206 }
207
208 @Override
209 public int hashCode() {
210 // N.B. - We deliberately use a garbage-free hashCode implementation (rather than e.g.
211 // Objects#hash). Depending on the structure of the graph being traversed, this method can
212 // be very hot.
213 return 31 * Objects.hashCode(dep) + rdep.hashCode();
214 }
215 }
216
shreyax19b43772018-05-18 11:45:44 -0700217 static class DepAndRdepAtDepth {
218 final DepAndRdep depAndRdep;
219 final int rdepDepth;
Nathan Harmata593dc522016-09-28 23:35:46 +0000220
shreyax19b43772018-05-18 11:45:44 -0700221 DepAndRdepAtDepth(DepAndRdep depAndRdep, int rdepDepth) {
nharmata398e6dab2018-04-12 15:31:26 -0700222 this.depAndRdep = depAndRdep;
223 this.rdepDepth = rdepDepth;
224 }
225 }
226
nharmata398e6dab2018-04-12 15:31:26 -0700227 /** Thread-safe {@link AggregateAllCallback} backed by a concurrent {@link Set}. */
228 @ThreadSafe
229 private static class ThreadSafeAggregateAllSkyKeysCallback
230 implements AggregateAllCallback<SkyKey, ImmutableSet<SkyKey>> {
231
232 private final Set<SkyKey> results;
233
234 private ThreadSafeAggregateAllSkyKeysCallback(int concurrencyLevel) {
235 this.results =
Googler45b59532018-05-03 11:10:20 -0700236 Collections.newSetFromMap(
237 new ConcurrentHashMap<>(
238 /*initialCapacity=*/ concurrencyLevel, /*loadFactor=*/ 0.75f));
nharmata398e6dab2018-04-12 15:31:26 -0700239 }
240
241 @Override
242 public void process(Iterable<SkyKey> partialResult)
243 throws QueryException, InterruptedException {
244 Iterables.addAll(results, partialResult);
245 }
246
247 @Override
248 public ImmutableSet<SkyKey> getResult() {
249 return ImmutableSet.copyOf(results);
250 }
251 }
Nathan Harmata593dc522016-09-28 23:35:46 +0000252}
253