Skip to content

Commit

Permalink
Introduce top-down action caching in Bazel. The top-down cache may be…
Browse files Browse the repository at this point in the history
… provided by a Bazel module.

The cache works by first computing a _transitive_ cache key for the action, known as a sketch. It composes the action keys for all dependent actions and all transitive source file digest hashes.

This feature is not currently wired up for use - consider it extremely experimental at this point.

RELNOTES: None
PiperOrigin-RevId: 260973078
  • Loading branch information
ericfelly authored and copybara-github committed Jul 31, 2019
1 parent d5f514b commit c5c078c
Show file tree
Hide file tree
Showing 17 changed files with 505 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ filegroup(
"//src/java_tools/singlejar:srcs",
"//src/main/cpp:srcs",
"//src/main/java/com/google/devtools/build/docgen:srcs",
"//src/main/java/com/google/devtools/build/lib/actionsketch:srcs",
"//src/main/java/com/google/devtools/build/lib:srcs",
"//src/main/java/com/google/devtools/build/lib/includescanning:srcs",
"//src/main/java/com/google/devtools/build/lib/network:srcs",
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/google/devtools/build/lib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/actions",
"//src/main/java/com/google/devtools/build/lib/actions:commandline_item",
"//src/main/java/com/google/devtools/build/lib/actions:localhost_capacity",
"//src/main/java/com/google/devtools/build/lib/actionsketch:action_sketch",
"//src/main/java/com/google/devtools/build/lib/analysis/platform",
"//src/main/java/com/google/devtools/build/lib/analysis/platform:platform_utils",
"//src/main/java/com/google/devtools/build/lib/analysis/platform:utils",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.devtools.build.skyframe.SkyValue;
import com.google.protobuf.ByteString;
import java.math.BigInteger;
import java.nio.ByteBuffer;
Expand All @@ -27,7 +28,7 @@
* all transitively consumed input files, as well as a transitive hash of all action keys.
*/
@AutoValue
public abstract class ActionSketch {
public abstract class ActionSketch implements SkyValue {
public static final int BIGINTEGER_ENCODED_LENGTH = /*length=*/ 1 + /*payload=*/ 17;
public static final int MAX_BYTES = /*hashes=*/ 2 * BIGINTEGER_ENCODED_LENGTH;

Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/google/devtools/build/lib/actionsketch/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package(
default_visibility = ["//src:__subpackages__"],
)

filegroup(
name = "srcs",
srcs = glob(["**"]),
)

java_library(
name = "action_sketch",
srcs = glob(["*.java"]),
deps = [
"//src/main/java/com/google/devtools/build/lib/actions",
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
"//third_party:auto_value",
"//third_party:guava",
"//third_party:jsr305",
"//third_party/protobuf:protobuf_java",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,7 @@ private Builder createBuilder(
.setEnabled(options.useActionCache)
.setVerboseExplanations(options.verboseExplanations)
.build()),
env.getTopDownActionCache(),
request.getPackageCacheOptions().checkOutputFiles
? modifiedOutputFiles
: ModifiedFileSet.NOTHING_MODIFIED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import com.google.devtools.build.lib.skyframe.Builder;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
import com.google.devtools.build.lib.skyframe.TopDownActionCache;
import com.google.devtools.build.lib.util.AbruptExitException;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.LoggingUtil;
Expand Down Expand Up @@ -76,16 +77,19 @@ public class SkyframeBuilder implements Builder {
private final MetadataProvider fileCache;
private final ActionInputPrefetcher actionInputPrefetcher;
private final ActionCacheChecker actionCacheChecker;
private final TopDownActionCache topDownActionCache;

@VisibleForTesting
public SkyframeBuilder(
SkyframeExecutor skyframeExecutor,
ActionCacheChecker actionCacheChecker,
TopDownActionCache topDownActionCache,
ModifiedFileSet modifiedOutputFiles,
MetadataProvider fileCache,
ActionInputPrefetcher actionInputPrefetcher) {
this.skyframeExecutor = skyframeExecutor;
this.actionCacheChecker = actionCacheChecker;
this.topDownActionCache = topDownActionCache;
this.modifiedOutputFiles = modifiedOutputFiles;
this.fileCache = fileCache;
this.actionInputPrefetcher = actionInputPrefetcher;
Expand Down Expand Up @@ -156,6 +160,7 @@ public void buildArtifacts(
exclusiveTests,
options,
actionCacheChecker,
topDownActionCache,
executionProgressReceiver,
topLevelArtifactContext);
// progressReceiver is finished, so unsynchronized access to builtTargets is now safe.
Expand Down Expand Up @@ -183,6 +188,7 @@ public void buildArtifacts(
exclusiveTest,
options,
actionCacheChecker,
topDownActionCache,
null,
topLevelArtifactContext);
exitCode =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.google.devtools.build.lib.packages.PackageFactory;
import com.google.devtools.build.lib.skyframe.AspectValue;
import com.google.devtools.build.lib.skyframe.PrecomputedValue;
import com.google.devtools.build.lib.skyframe.TopDownActionCache;
import com.google.devtools.build.lib.util.AbruptExitException;
import com.google.devtools.build.lib.util.io.OutErr;
import com.google.devtools.build.lib.vfs.DigestHashFunction.DefaultHashFunctionNotSetException;
Expand Down Expand Up @@ -93,6 +94,17 @@ public ModuleFileSystem getFileSystem(
return null;
}

/**
* Returns the {@link TopDownActionCache} used by Bazel. It is an error if more than one module
* returns a top-down action cache. If all modules return null, there will be no top-down caching.
*
* <p>This method will be called at the beginning of Bazel startup (in-between {@link #globalInit}
* and {@link #blazeStartup}).
*/
public TopDownActionCache getTopDownActionCache() {
return null;
}

/** Tuple returned by {@link #getFileSystem}. */
@AutoValue
public abstract static class ModuleFileSystem {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
import com.google.devtools.build.lib.skyframe.SkyframeBuildView;
import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
import com.google.devtools.build.lib.skyframe.TopDownActionCache;
import com.google.devtools.build.lib.util.AbruptExitException;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.io.OutErr;
Expand Down Expand Up @@ -87,6 +88,7 @@ public final class CommandEnvironment {
private PathFragment relativeWorkingDirectory = PathFragment.EMPTY_FRAGMENT;
private long commandStartTime;
private OutputService outputService;
private TopDownActionCache topDownActionCache;
private Path workingDirectory;
private String workspaceName;
private boolean haveSetupPackageCache = false;
Expand Down Expand Up @@ -490,6 +492,11 @@ public ActionCache getPersistentActionCache() throws IOException {
return workspace.getPersistentActionCache(reporter);
}

/** Returns the top-down action cache to use, or null. */
public TopDownActionCache getTopDownActionCache() {
return topDownActionCache;
}

/**
* An array of String values useful if Blaze crashes. For now, just returns the build id as soon
* as it is determined.
Expand Down Expand Up @@ -633,6 +640,8 @@ void beforeCommand(long waitTimeInMs, InvocationPolicy invocationPolicy)

outputService = null;
BlazeModule outputModule = null;
topDownActionCache = null;
BlazeModule topDownCachingModule = null;
if (command.builds()) {
for (BlazeModule module : runtime.getBlazeModules()) {
OutputService moduleService = module.getOutputService();
Expand All @@ -646,6 +655,18 @@ void beforeCommand(long waitTimeInMs, InvocationPolicy invocationPolicy)
outputService = moduleService;
outputModule = module;
}

TopDownActionCache moduleCache = module.getTopDownActionCache();
if (moduleCache != null) {
if (topDownActionCache != null) {
throw new IllegalStateException(
String.format(
"More than one module (%s and %s) returns a top down action cache",
module.getClass(), topDownCachingModule.getClass()));
}
topDownActionCache = moduleCache;
topDownCachingModule = module;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import com.google.devtools.build.lib.actions.MissingInputFileException;
import com.google.devtools.build.lib.actions.NotifyOnActionCacheHit;
import com.google.devtools.build.lib.actions.PackageRootResolver;
import com.google.devtools.build.lib.actionsketch.ActionSketch;
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.causes.Cause;
import com.google.devtools.build.lib.causes.LabelCause;
Expand Down Expand Up @@ -100,6 +101,7 @@
* </ol>
*/
public class ActionExecutionFunction implements SkyFunction {

private final ActionRewindStrategy actionRewindStrategy = new ActionRewindStrategy();
private final SkyframeActionExecutor skyframeActionExecutor;
private final BlazeDirectories directories;
Expand Down Expand Up @@ -154,6 +156,19 @@ public SkyValue compute(SkyKey skyKey, Environment env)
}
}

ActionSketch sketch = null;
TopDownActionCache topDownActionCache = skyframeActionExecutor.getTopDownActionCache();
if (topDownActionCache != null) {
sketch = (ActionSketch) env.getValue(ActionSketchFunction.key(actionLookupData));
if (sketch == null) {
return null;
}
ActionExecutionValue actionExecutionValue = topDownActionCache.get(sketch);
if (actionExecutionValue != null) {
return actionExecutionValue.transformForSharedAction(action.getOutputs());
}
}

// For restarts of this ActionExecutionFunction we use a ContinuationState variable, below, to
// avoid redoing work.
//
Expand Down Expand Up @@ -289,6 +304,9 @@ public SkyValue compute(SkyKey skyKey, Environment env)

// Remove action from state map in case it's there (won't be unless it discovers inputs).
stateMap.remove(action);
if (sketch != null && result.dataIsShareable()) {
topDownActionCache.put(sketch, result);
}
return result;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright 2019 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.skyframe;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionKeyContext;
import com.google.devtools.build.lib.actions.ActionLookupData;
import com.google.devtools.build.lib.actions.ActionLookupValue;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.DerivedArtifact;
import com.google.devtools.build.lib.actions.FileArtifactValue;
import com.google.devtools.build.lib.actionsketch.ActionSketch;
import com.google.devtools.build.lib.actionsketch.Sketches;
import com.google.devtools.build.lib.concurrent.BlazeInterners;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.util.BigIntegerFingerprintUtils;
import com.google.devtools.build.skyframe.AbstractSkyKey;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/**
* {@link ActionSketchFunction} computes an {@link ActionSketch} for the given Action. This is a
* transitive hash of the dependent action keys and source file content hashes.
*/
public final class ActionSketchFunction implements SkyFunction {
private final ActionKeyContext actionKeyContext;

public ActionSketchFunction(ActionKeyContext actionKeyContext) {
this.actionKeyContext = actionKeyContext;
}

public static SketchKey key(ActionLookupData key) {
return SketchKey.create(key);
}

@AutoCodec.VisibleForSerialization
@AutoCodec
static class SketchKey extends AbstractSkyKey<ActionLookupData> {
private static final LoadingCache<ActionLookupData, SketchKey> keyCache =
CacheBuilder.newBuilder()
.weakKeys()
.concurrencyLevel(BlazeInterners.concurrencyLevel())
.build(CacheLoader.from(SketchKey::new));

private SketchKey(ActionLookupData arg) {
super(arg);
}

@AutoCodec.VisibleForSerialization
@AutoCodec.Instantiator
static SketchKey create(ActionLookupData arg) {
return keyCache.getUnchecked(arg);
}

@Override
public SkyFunctionName functionName() {
return SkyFunctions.ACTION_SKETCH;
}
}

@Nullable
@Override
public String extractTag(SkyKey skyKey) {
return null;
}

@Nullable
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
ActionLookupData actionLookupData = (ActionLookupData) skyKey.argument();
ActionLookupValue actionLookupValue =
ArtifactFunction.getActionLookupValue(actionLookupData.getActionLookupKey(), env);
if (actionLookupValue == null) {
return null;
}

Action action = actionLookupValue.getAction(actionLookupData.getActionIndex());
List<Artifact> srcArtifacts = new ArrayList<>();
List<SketchKey> depActions = new ArrayList<>();
for (Artifact artifact : action.getInputs()) {
if (artifact.isSourceArtifact()) {
srcArtifacts.add(artifact);
} else {
depActions.add(SketchKey.create(((DerivedArtifact) artifact).getGeneratingActionKey()));
}
}

Map<SkyKey, SkyValue> srcArtifactValues = env.getValues(srcArtifacts);
Map<SkyKey, SkyValue> depSketchValues = env.getValues(depActions);
if (env.valuesMissing()) {
return null;
}

BigInteger transitiveActionKeyHash = Sketches.computeActionKey(action, actionKeyContext);
BigInteger transitiveSourceHash = BigInteger.ZERO;

// Incorporate the direct source values.
for (SkyValue val : srcArtifactValues.values()) {
FileArtifactValue fileArtifactValue = (FileArtifactValue) val;
transitiveSourceHash =
BigIntegerFingerprintUtils.compose(
transitiveSourceHash, fileArtifactValue.getValueFingerprint());
}

// Incorporate the transitive action key and source values.
for (SkyValue sketchVal : depSketchValues.values()) {
ActionSketch depSketch = (ActionSketch) sketchVal;
transitiveActionKeyHash =
BigIntegerFingerprintUtils.compose(
transitiveActionKeyHash, depSketch.transitiveActionLookupHash());
transitiveSourceHash =
BigIntegerFingerprintUtils.compose(
transitiveSourceHash, depSketch.transitiveSourceHash());
}

return ActionSketch.builder()
.setTransitiveActionLookupHash(transitiveActionKeyHash)
.setTransitiveSourceHash(transitiveSourceHash)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public final class SkyFunctions {
SkyFunctionName.createNonHermetic("PRECOMPUTED");
public static final SkyFunctionName CLIENT_ENVIRONMENT_VARIABLE =
SkyFunctionName.createNonHermetic("CLIENT_ENVIRONMENT_VARIABLE");
static final SkyFunctionName ACTION_SKETCH = SkyFunctionName.createHermetic("ACTION_SKETCH");
public static final SkyFunctionName ACTION_ENVIRONMENT_VARIABLE =
SkyFunctionName.createHermetic("ACTION_ENVIRONMENT_VARIABLE");
public static final SkyFunctionName DIRECTORY_LISTING_STATE =
Expand Down
Loading

0 comments on commit c5c078c

Please sign in to comment.