Skip to content

Commit f63ce79

Browse files
tjgqcopybara-github
authored andcommitted
Avoid unnecessary copying when building Merkle trees.
Instead of accumulating a single set of children in DirectoryTreeBuilder and later splitting it up into file, symlink and subdirectory sets, we can accumulate the latter directly. Closes bazelbuild#17912. PiperOrigin-RevId: 520585350 Change-Id: I02b26825976c72d59462a66ffd9afaec3d7c4176
1 parent 6146e4a commit f63ce79

File tree

3 files changed

+45
-41
lines changed

3 files changed

+45
-41
lines changed

src/main/java/com/google/devtools/build/lib/remote/merkletree/DirectoryTree.java

+32-27
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,15 @@
1616
import build.bazel.remote.execution.v2.Digest;
1717
import com.google.common.base.Preconditions;
1818
import com.google.common.collect.Iterables;
19-
import com.google.common.collect.Sets;
2019
import com.google.devtools.build.lib.vfs.Path;
2120
import com.google.devtools.build.lib.vfs.PathFragment;
21+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2222
import com.google.protobuf.ByteString;
23-
import java.util.ArrayList;
2423
import java.util.HashMap;
25-
import java.util.List;
2624
import java.util.Map;
2725
import java.util.Objects;
2826
import java.util.SortedSet;
27+
import java.util.TreeSet;
2928

3029
/**
3130
* Intermediate tree representation of a list of lexicographically sorted list of files. Each node
@@ -34,11 +33,12 @@
3433
final class DirectoryTree {
3534

3635
interface Visitor {
36+
3737
void visitDirectory(
3838
PathFragment dirname,
39-
List<FileNode> files,
40-
List<SymlinkNode> symlinks,
41-
List<DirectoryNode> dirs);
39+
SortedSet<FileNode> files,
40+
SortedSet<SymlinkNode> symlinks,
41+
SortedSet<DirectoryNode> dirs);
4242
}
4343

4444
abstract static class Node implements Comparable<Node> {
@@ -204,26 +204,44 @@ public String toString() {
204204
}
205205

206206
static class DirectoryNode extends Node {
207-
private final SortedSet<Node> children = Sets.newTreeSet();
207+
208+
private final SortedSet<FileNode> files = new TreeSet<>();
209+
private final SortedSet<SymlinkNode> symlinks = new TreeSet<>();
210+
private final SortedSet<DirectoryNode> subdirs = new TreeSet<>();
208211

209212
DirectoryNode(String pathSegment) {
210213
super(pathSegment);
211214
}
212215

213-
boolean addChild(Node child) {
214-
return children.add(Preconditions.checkNotNull(child, "child"));
216+
@CanIgnoreReturnValue
217+
boolean addChild(FileNode file) {
218+
return files.add(Preconditions.checkNotNull(file, "file"));
219+
}
220+
221+
@CanIgnoreReturnValue
222+
boolean addChild(SymlinkNode symlink) {
223+
return symlinks.add(Preconditions.checkNotNull(symlink, "symlink"));
224+
}
225+
226+
@CanIgnoreReturnValue
227+
boolean addChild(DirectoryNode subdir) {
228+
return subdirs.add(Preconditions.checkNotNull(subdir, "subdir"));
215229
}
216230

217231
@Override
218232
public int hashCode() {
219-
return Objects.hash(super.hashCode(), children.hashCode());
233+
return Objects.hash(
234+
super.hashCode(), files.hashCode(), symlinks.hashCode(), subdirs.hashCode());
220235
}
221236

222237
@Override
223238
public boolean equals(Object o) {
224239
if (o instanceof DirectoryNode) {
225240
DirectoryNode other = (DirectoryNode) o;
226-
return super.equals(other) && Objects.equals(children, other.children);
241+
return super.equals(other)
242+
&& Objects.equals(files, other.files)
243+
&& Objects.equals(symlinks, other.symlinks)
244+
&& Objects.equals(subdirs, other.subdirs);
227245
}
228246
return false;
229247
}
@@ -265,23 +283,10 @@ private void visit(Visitor visitor, PathFragment dirname) {
265283
return;
266284
}
267285

268-
List<FileNode> files = new ArrayList<>(dir.children.size());
269-
List<SymlinkNode> symlinks = new ArrayList<>();
270-
List<DirectoryNode> dirs = new ArrayList<>();
271-
for (Node child : dir.children) {
272-
if (child instanceof FileNode) {
273-
files.add((FileNode) child);
274-
} else if (child instanceof SymlinkNode) {
275-
symlinks.add((SymlinkNode) child);
276-
} else if (child instanceof DirectoryNode) {
277-
dirs.add((DirectoryNode) child);
278-
visit(visitor, dirname.getRelative(child.pathSegment));
279-
} else {
280-
throw new IllegalStateException(
281-
String.format("Node type '%s' is not supported", child.getClass().getSimpleName()));
282-
}
286+
for (DirectoryNode subdir : dir.subdirs) {
287+
visit(visitor, dirname.getRelative(subdir.getPathSegment()));
283288
}
284-
visitor.visitDirectory(dirname, files, symlinks, dirs);
289+
visitor.visitDirectory(dirname, dir.files, dir.symlinks, dir.subdirs);
285290
}
286291

287292
@Override

src/main/java/com/google/devtools/build/lib/remote/merkletree/MerkleTree.java

+3-5
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import com.google.common.collect.Iterables;
2929
import com.google.common.collect.Maps;
3030
import com.google.common.collect.Multimap;
31-
import com.google.common.collect.Sets;
3231
import com.google.devtools.build.lib.actions.ActionInput;
3332
import com.google.devtools.build.lib.actions.ArtifactPathResolver;
3433
import com.google.devtools.build.lib.actions.MetadataProvider;
@@ -299,8 +298,7 @@ private static MerkleTree build(DirectoryTree tree, DigestUtil digestUtil) {
299298
m.remove(subDirname), "subMerkleTree at '%s' was null", subDirname);
300299
subDirs.put(dir.getPathSegment(), subMerkleTree);
301300
}
302-
MerkleTree mt =
303-
buildMerkleTree(new TreeSet<>(files), new TreeSet<>(symlinks), subDirs, digestUtil);
301+
MerkleTree mt = buildMerkleTree(files, symlinks, subDirs, digestUtil);
304302
m.put(dirname, mt);
305303
});
306304
MerkleTree rootMerkleTree = m.get(PathFragment.EMPTY_FRAGMENT);
@@ -326,11 +324,11 @@ public static MerkleTree merge(Collection<MerkleTree> merkleTrees, DigestUtil di
326324
}
327325

328326
// Some differ, do a full merge.
329-
SortedSet<DirectoryTree.FileNode> files = Sets.newTreeSet();
327+
SortedSet<DirectoryTree.FileNode> files = new TreeSet<>();
330328
for (MerkleTree merkleTree : merkleTrees) {
331329
files.addAll(merkleTree.getFiles());
332330
}
333-
SortedSet<DirectoryTree.SymlinkNode> symlinks = Sets.newTreeSet();
331+
SortedSet<DirectoryTree.SymlinkNode> symlinks = new TreeSet<>();
334332
for (MerkleTree merkleTree : merkleTrees) {
335333
symlinks.addAll(merkleTree.getSymlinks());
336334
}

src/test/java/com/google/devtools/build/lib/remote/merkletree/DirectoryTreeTest.java

+10-9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.io.IOException;
3434
import java.util.ArrayList;
3535
import java.util.List;
36+
import java.util.SortedSet;
3637
import java.util.stream.Collectors;
3738
import org.junit.Before;
3839
import org.junit.Test;
@@ -121,9 +122,9 @@ static void assertLexicographicalOrder(DirectoryTree tree) {
121122
// Assert the lexicographical order as defined by the remote execution protocol
122123
tree.visit(
123124
(PathFragment dirname,
124-
List<FileNode> files,
125-
List<SymlinkNode> symlinks,
126-
List<DirectoryNode> dirs) -> {
125+
SortedSet<FileNode> files,
126+
SortedSet<SymlinkNode> symlinks,
127+
SortedSet<DirectoryNode> dirs) -> {
127128
assertThat(files).isInStrictOrder();
128129
assertThat(dirs).isInStrictOrder();
129130
});
@@ -141,9 +142,9 @@ private static List<DirectoryNode> directoryNodesAtDepth(DirectoryTree tree, int
141142
List<DirectoryNode> directoryNodes = new ArrayList<>();
142143
tree.visit(
143144
(PathFragment dirname,
144-
List<FileNode> files,
145-
List<SymlinkNode> symlinks,
146-
List<DirectoryNode> dirs) -> {
145+
SortedSet<FileNode> files,
146+
SortedSet<SymlinkNode> symlinks,
147+
SortedSet<DirectoryNode> dirs) -> {
147148
int currDepth = dirname.segmentCount();
148149
if (currDepth == depth) {
149150
directoryNodes.addAll(dirs);
@@ -156,9 +157,9 @@ static List<FileNode> fileNodesAtDepth(DirectoryTree tree, int depth) {
156157
List<FileNode> fileNodes = new ArrayList<>();
157158
tree.visit(
158159
(PathFragment dirname,
159-
List<FileNode> files,
160-
List<SymlinkNode> symlinks,
161-
List<DirectoryNode> dirs) -> {
160+
SortedSet<FileNode> files,
161+
SortedSet<SymlinkNode> symlinks,
162+
SortedSet<DirectoryNode> dirs) -> {
162163
int currDepth = dirname.segmentCount();
163164
if (currDepth == depth) {
164165
fileNodes.addAll(files);

0 commit comments

Comments
 (0)