Store runfiles as parallel lists instead of a list of pairs.
The cost of the `Pair` object is greater than the additional array cost of storing the artifacts and metadata in separate lists.
Getter methods for lists of pairs on `RunfilesArtifactValue` are replaced with functional-style `forEach` methods.
PiperOrigin-RevId: 515635185
Change-Id: Iee0757f47b5cf202100a71d1a708682db9cc16a1
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionInputMapHelper.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionInputMapHelper.java
index 10eb5f6..cddb489 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionInputMapHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionInputMapHelper.java
@@ -28,7 +28,6 @@
import com.google.devtools.build.lib.actions.FileArtifactValue;
import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
import com.google.devtools.build.lib.analysis.actions.SymlinkAction;
-import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.skyframe.SkyFunction.Environment;
import com.google.devtools.build.skyframe.SkyValue;
import java.util.Map;
@@ -85,33 +84,34 @@
// Instead, the way the SpawnInputExpander expands runfiles is via the Runfiles class
// which contains all artifacts in the runfiles tree minus the MANIFEST file.
RunfilesArtifactValue runfilesArtifactValue = (RunfilesArtifactValue) value;
- for (Pair<Artifact, FileArtifactValue> entry : runfilesArtifactValue.getFileArtifacts()) {
- Artifact artifact = entry.first;
- inputMap.put(artifact, entry.getSecond(), /*depOwner=*/ key);
- if (artifact.isFileset()) {
- ImmutableList<FilesetOutputSymlink> expandedFileset =
- getFilesets(env, (SpecialArtifact) artifact);
- if (expandedFileset != null) {
- filesetsInsideRunfiles.put(artifact, expandedFileset);
- consumer.accumulate(expandedFileset);
- }
- } else {
- consumer.accumulate(entry.getSecond());
- }
- }
- for (Pair<Artifact, TreeArtifactValue> entry : runfilesArtifactValue.getTreeArtifacts()) {
- expandTreeArtifactAndPopulateArtifactData(
- entry.getFirst(),
- Preconditions.checkNotNull(entry.getSecond()),
- expandedArtifacts,
- archivedTreeArtifacts,
- inputMap,
- /*depOwner=*/ key);
- consumer.accumulate(entry.getSecond());
- }
+ runfilesArtifactValue.forEachFile(
+ (artifact, metadata) -> {
+ inputMap.put(artifact, metadata, /* depOwner= */ key);
+ if (artifact.isFileset()) {
+ ImmutableList<FilesetOutputSymlink> expandedFileset =
+ getFilesets(env, (SpecialArtifact) artifact);
+ if (expandedFileset != null) {
+ filesetsInsideRunfiles.put(artifact, expandedFileset);
+ consumer.accumulate(expandedFileset);
+ }
+ } else {
+ consumer.accumulate(metadata);
+ }
+ });
+ runfilesArtifactValue.forEachTree(
+ (treeArtifact, metadata) -> {
+ expandTreeArtifactAndPopulateArtifactData(
+ treeArtifact,
+ metadata,
+ expandedArtifacts,
+ archivedTreeArtifacts,
+ inputMap,
+ /* depOwner= */ key);
+ consumer.accumulate(metadata);
+ });
// We have to cache the "digest" of the aggregating value itself, because the action cache
// checker may want it.
- inputMap.put(key, runfilesArtifactValue.getMetadata(), /*depOwner=*/ key);
+ inputMap.put(key, runfilesArtifactValue.getMetadata(), /* depOwner= */ key);
} else if (value instanceof TreeArtifactValue) {
TreeArtifactValue treeArtifactValue = (TreeArtifactValue) value;
expandTreeArtifactAndPopulateArtifactData(
@@ -154,7 +154,7 @@
}
@Nullable
- static ImmutableList<FilesetOutputSymlink> getFilesets(
+ private static ImmutableList<FilesetOutputSymlink> getFilesets(
Environment env, SpecialArtifact actionInput) throws InterruptedException {
Preconditions.checkState(actionInput.isFileset(), actionInput);
ActionLookupData generatingActionKey = actionInput.getGeneratingActionKey();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java
index a701a9f..8c2a068 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java
@@ -46,7 +46,6 @@
import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFile;
import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.Fingerprint;
-import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.lib.vfs.XattrProvider;
@@ -57,8 +56,6 @@
import com.google.devtools.build.skyframe.SkyValue;
import com.google.devtools.build.skyframe.SkyframeLookupResult;
import java.io.IOException;
-import java.util.Comparator;
-import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import javax.annotation.Nullable;
@@ -353,28 +350,34 @@
FileArtifactValue value,
SkyFunction.Environment env)
throws InterruptedException {
- ImmutableList.Builder<Pair<Artifact, FileArtifactValue>> fileInputsBuilder =
- ImmutableList.builder();
- ImmutableList.Builder<Pair<Artifact, TreeArtifactValue>> directoryInputsBuilder =
- ImmutableList.builder();
- List<Artifact> inputs = action.getInputs().toList();
+ ImmutableList<Artifact> inputs = action.getInputs().toList();
SkyframeLookupResult values = env.getValuesAndExceptions(Artifact.keys(inputs));
if (env.valuesMissing()) {
return null;
}
- for (Artifact input : inputs) {
+
+ ImmutableList.Builder<Artifact> files = ImmutableList.builder();
+ ImmutableList.Builder<FileArtifactValue> fileValues = ImmutableList.builder();
+ ImmutableList.Builder<Artifact> trees = ImmutableList.builder();
+ ImmutableList.Builder<TreeArtifactValue> treeValues = ImmutableList.builder();
+
+ // Sort for better equality in RunfilesArtifactValue.
+ ImmutableList<Artifact> sortedInputs =
+ ImmutableList.sortedCopyOf(Artifact.EXEC_PATH_COMPARATOR, inputs);
+ for (Artifact input : sortedInputs) {
SkyValue inputValue = values.get(Artifact.key(input));
if (inputValue == null) {
return null;
}
if (inputValue instanceof FileArtifactValue) {
- fileInputsBuilder.add(Pair.of(input, (FileArtifactValue) inputValue));
+ files.add(input);
+ fileValues.add((FileArtifactValue) inputValue);
} else if (inputValue instanceof ActionExecutionValue) {
- fileInputsBuilder.add(
- Pair.of(
- input, ((ActionExecutionValue) inputValue).getExistingFileArtifactValue(input)));
+ files.add(input);
+ fileValues.add(((ActionExecutionValue) inputValue).getExistingFileArtifactValue(input));
} else if (inputValue instanceof TreeArtifactValue) {
- directoryInputsBuilder.add(Pair.of(input, (TreeArtifactValue) inputValue));
+ trees.add(input);
+ treeValues.add((TreeArtifactValue) inputValue);
} else {
// We do not recurse in middleman artifacts.
Preconditions.checkState(
@@ -386,16 +389,8 @@
}
}
- ImmutableList<Pair<Artifact, FileArtifactValue>> fileInputs =
- ImmutableList.sortedCopyOf(
- Comparator.comparing(pair -> pair.getFirst().getExecPathString()),
- fileInputsBuilder.build());
- ImmutableList<Pair<Artifact, TreeArtifactValue>> directoryInputs =
- ImmutableList.sortedCopyOf(
- Comparator.comparing(pair -> pair.getFirst().getExecPathString()),
- directoryInputsBuilder.build());
-
- return new RunfilesArtifactValue(fileInputs, directoryInputs, value);
+ return new RunfilesArtifactValue(
+ value, files.build(), fileValues.build(), trees.build(), treeValues.build());
}
/**
@@ -413,7 +408,7 @@
}
@Nullable
- public static ActionLookupValue getActionLookupValue(
+ static ActionLookupValue getActionLookupValue(
ActionLookupKey actionLookupKey, SkyFunction.Environment env) throws InterruptedException {
ActionLookupValue value = (ActionLookupValue) env.getValue(actionLookupKey);
if (value == null) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
index 2073969..6ae08bb 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -488,7 +488,6 @@
"//src/main/java/com/google/devtools/build/lib/actions:file_metadata",
"//src/main/java/com/google/devtools/build/lib/actions:fileset_output_symlink",
"//src/main/java/com/google/devtools/build/lib/analysis:actions/symlink_action",
- "//src/main/java/com/google/devtools/build/lib/util",
"//src/main/java/com/google/devtools/build/skyframe",
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
"//third_party:guava",
@@ -589,7 +588,7 @@
":tree_artifact_value",
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
"//src/main/java/com/google/devtools/build/lib/actions:file_metadata",
- "//src/main/java/com/google/devtools/build/lib/util",
+ "//src/main/java/com/google/devtools/build/lib/util:hash_codes",
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
"//third_party:guava",
],
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/RunfilesArtifactValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/RunfilesArtifactValue.java
index afe3c78..ec76bfc 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/RunfilesArtifactValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/RunfilesArtifactValue.java
@@ -13,41 +13,49 @@
// limitations under the License.
package com.google.devtools.build.lib.skyframe;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.FileArtifactValue;
-import com.google.devtools.build.lib.util.Pair;
+import com.google.devtools.build.lib.util.HashCodes;
import com.google.devtools.build.skyframe.SkyValue;
-import java.util.Collection;
/** The artifacts behind a runfiles middleman. */
-class RunfilesArtifactValue implements SkyValue {
- private final ImmutableList<Pair<Artifact, FileArtifactValue>> fileInputs;
- private final ImmutableList<Pair<Artifact, TreeArtifactValue>> directoryInputs;
+final class RunfilesArtifactValue implements SkyValue {
+
+ @FunctionalInterface
+ interface RunfilesConsumer<T> {
+ void accept(Artifact artifact, T metadata) throws InterruptedException;
+ }
+
private final FileArtifactValue metadata;
+ // Parallel lists.
+ private final ImmutableList<Artifact> files;
+ private final ImmutableList<FileArtifactValue> fileValues;
+
+ // Parallel lists.
+ private final ImmutableList<Artifact> trees;
+ private final ImmutableList<TreeArtifactValue> treeValues;
+
RunfilesArtifactValue(
- ImmutableList<Pair<Artifact, FileArtifactValue>> fileInputs,
- ImmutableList<Pair<Artifact, TreeArtifactValue>> directoryInputs,
- FileArtifactValue metadata) {
- this.fileInputs = Preconditions.checkNotNull(fileInputs);
- this.directoryInputs = Preconditions.checkNotNull(directoryInputs);
- this.metadata = Preconditions.checkNotNull(metadata);
- }
-
- /** Returns the none tree artifacts that this artifact expands to, together with their data. */
- Collection<Pair<Artifact, FileArtifactValue>> getFileArtifacts() {
- return fileInputs;
- }
-
- /**
- * Returns the tree artifacts that this artifact expands to, together with the information to
- * which artifacts the tree artifacts expand to.
- */
- Collection<Pair<Artifact, TreeArtifactValue>> getTreeArtifacts() {
- return directoryInputs;
+ FileArtifactValue metadata,
+ ImmutableList<Artifact> files,
+ ImmutableList<FileArtifactValue> fileValues,
+ ImmutableList<Artifact> trees,
+ ImmutableList<TreeArtifactValue> treeValues) {
+ this.metadata = checkNotNull(metadata);
+ this.files = checkNotNull(files);
+ this.fileValues = checkNotNull(fileValues);
+ this.trees = checkNotNull(trees);
+ this.treeValues = checkNotNull(treeValues);
+ checkArgument(
+ files.size() == fileValues.size() && trees.size() == treeValues.size(),
+ "Size mismatch: %s",
+ this);
}
/** Returns the data of the artifact for this value, as computed by the action cache checker. */
@@ -55,31 +63,49 @@
return metadata;
}
- @SuppressWarnings("EqualsGetClass") // RunfilesArtifactValue not equal to Aggregating.
+ /** Visits the file artifacts that this runfiles artifact expands to, together with their data. */
+ void forEachFile(RunfilesConsumer<FileArtifactValue> consumer) throws InterruptedException {
+ for (int i = 0; i < files.size(); i++) {
+ consumer.accept(files.get(i), fileValues.get(i));
+ }
+ }
+
+ /** Visits the tree artifacts that this runfiles artifact expands to, together with their data. */
+ void forEachTree(RunfilesConsumer<TreeArtifactValue> consumer) throws InterruptedException {
+ for (int i = 0; i < trees.size(); i++) {
+ consumer.accept(trees.get(i), treeValues.get(i));
+ }
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
- if (o == null || getClass() != o.getClass()) {
+ if (!(o instanceof RunfilesArtifactValue)) {
return false;
}
RunfilesArtifactValue that = (RunfilesArtifactValue) o;
return metadata.equals(that.metadata)
- && fileInputs.equals(that.fileInputs)
- && directoryInputs.equals(that.directoryInputs);
+ && files.equals(that.files)
+ && fileValues.equals(that.fileValues)
+ && trees.equals(that.trees)
+ && treeValues.equals(that.treeValues);
}
@Override
public int hashCode() {
- return 31 * 31 * directoryInputs.hashCode() + 31 * fileInputs.hashCode() + metadata.hashCode();
+ return HashCodes.hashObjects(metadata, files, fileValues, trees, treeValues);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
- .add("fileInputs", fileInputs)
- .add("directoryInputs", directoryInputs)
+ .add("metadata", metadata)
+ .add("files", files)
+ .add("fileValues", fileValues)
+ .add("trees", trees)
+ .add("treeValues", treeValues)
.toString();
}
}