Batch the recursive GlobValue dep with the DirectoryListingValue dep.
This is a strict win wrt parallelism, at the cost of code ugliness. Now it takes less total Skyframe overhead to get to the recursive per-dirent work. Previously, we had to sequentially wait for a Skyframe thread to be available for the recursive GlobValue dep, and then a Skyframe thread to be available for the GlobValue restart, and then a Skyframe thread to be available for the DirectoryListingValue work.
RELNOTES: None
PiperOrigin-RevId: 236182538
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java
index 5820ac8..cabb54f 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.skyframe;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.devtools.build.lib.actions.FileValue;
@@ -98,33 +99,9 @@
boolean globMatchesBareFile = patternTail == null;
- // "**" also matches an empty segment, so try the case where it is not present.
- if ("**".equals(patternHead)) {
- if (globMatchesBareFile) {
- // Recursive globs aren't supposed to match the package's directory.
- if (!glob.excludeDirs() && !globSubdir.equals(PathFragment.EMPTY_FRAGMENT)) {
- matches.add(globSubdir);
- }
- } else {
- SkyKey globKey =
- GlobValue.internalKey(
- glob.getPackageId(),
- glob.getPackageRoot(),
- globSubdir,
- patternTail,
- glob.excludeDirs());
- GlobValue globValue = (GlobValue) env.getValue(globKey);
- if (globValue == null) {
- return null;
- }
- matches.addTransitive(globValue.getMatches());
- }
- }
-
PathFragment dirPathFragment = glob.getPackageId().getPackageFragment().getRelative(globSubdir);
RootedPath dirRootedPath = RootedPath.toRootedPath(glob.getPackageRoot(), dirPathFragment);
if (alwaysUseDirListing || containsGlobs(patternHead)) {
- String subdirPattern = "**".equals(patternHead) ? glob.getPattern() : patternTail;
// Pattern contains globs, so a directory listing is required.
//
// Note that we have good reason to believe the directory exists: if this is the
@@ -132,13 +109,51 @@
// existence; if this is a lower-level directory in the package, then we got here from
// previous directory listings. Filesystem operations concurrent with build could mean the
// directory no longer exists, but DirectoryListingFunction handles that gracefully.
- DirectoryListingValue listingValue = (DirectoryListingValue)
- env.getValue(DirectoryListingValue.key(dirRootedPath));
- if (listingValue == null) {
- return null;
+ SkyKey directoryListingKey = DirectoryListingValue.key(dirRootedPath);
+ DirectoryListingValue listingValue = null;
+
+ boolean patternHeadIsStarStar = "**".equals(patternHead);
+ if (patternHeadIsStarStar) {
+ // "**" also matches an empty segment, so try the case where it is not present.
+ if (globMatchesBareFile) {
+ // Recursive globs aren't supposed to match the package's directory.
+ if (!glob.excludeDirs() && !globSubdir.equals(PathFragment.EMPTY_FRAGMENT)) {
+ matches.add(globSubdir);
+ }
+ } else {
+ // Optimize away a Skyframe restart by requesting the DirectoryListingValue dep and
+ // recursive GlobValue dep in a single batch.
+
+ SkyKey keyForRecursiveGlobInCurrentDirectory =
+ GlobValue.internalKey(
+ glob.getPackageId(),
+ glob.getPackageRoot(),
+ globSubdir,
+ patternTail,
+ glob.excludeDirs());
+ Map<SkyKey, SkyValue> listingAndRecursiveGlobMap =
+ env.getValues(
+ ImmutableList.of(keyForRecursiveGlobInCurrentDirectory, directoryListingKey));
+ if (env.valuesMissing()) {
+ return null;
+ }
+ GlobValue globValue =
+ (GlobValue) listingAndRecursiveGlobMap.get(keyForRecursiveGlobInCurrentDirectory);
+ matches.addTransitive(globValue.getMatches());
+ listingValue =
+ (DirectoryListingValue) listingAndRecursiveGlobMap.get(directoryListingKey);
+ }
}
- // In order to batch Skyframe requests, we do three passes over the directory:
+ if (listingValue == null) {
+ listingValue = (DirectoryListingValue) env.getValue(directoryListingKey);
+ if (listingValue == null) {
+ return null;
+ }
+ }
+
+ // Now that we have the directory listing, we do three passes over it so as to maximize
+ // skyframe batching:
// (1) Process every dirent, keeping track of values we need to request if the dirent cannot
// be processed with current information (symlink targets and subdirectory globs/package
// lookups for some subdirectories).
@@ -149,6 +164,7 @@
Map<SkyKey, Dirent> symlinkFileMap = Maps.newHashMapWithExpectedSize(direntsSize);
Map<SkyKey, Dirent> subdirMap = Maps.newHashMapWithExpectedSize(direntsSize);
Map<Dirent, Object> sortedResultMap = Maps.newTreeMap();
+ String subdirPattern = patternHeadIsStarStar ? glob.getPattern() : patternTail;
// First pass: do normal files and collect SkyKeys to request for subdirectories and symlinks.
for (Dirent dirent : listingValue.getDirents()) {
Dirent.Type direntType = dirent.getType();