Skip to content

Commit 8fd0d1d

Browse files
fmeumcopybara-github
authored andcommitted
Path map command lines lazily during expansion
This removes a peak memory inefficiency of path mapping compared to regular execution. Closes bazelbuild#25368. PiperOrigin-RevId: 747933848 Change-Id: Ib2d8ffb626b8181dd7d831d9a5207bd13b581420
1 parent 98cc220 commit 8fd0d1d

File tree

11 files changed

+494
-145
lines changed

11 files changed

+494
-145
lines changed

src/main/java/com/google/devtools/build/lib/actions/ArgChunk.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ public interface ArgChunk {
2828
* <p>The returned {@link Iterable} may lazily materialize strings during iteration, so consumers
2929
* should attempt to avoid iterating more times than necessary.
3030
*/
31-
Iterable<String> arguments();
31+
Iterable<String> arguments(PathMapper pathMapper);
3232

3333
/**
3434
* Counts the total length of all arguments in this chunk.
3535
*
3636
* <p>Implementations that lazily materialize strings may be able to compute the total argument
3737
* length without actually materializing the arguments.
3838
*/
39-
int totalArgLength();
39+
int totalArgLength(PathMapper pathMapper);
4040
}

src/main/java/com/google/devtools/build/lib/actions/BUILD

+1-9
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ java_library(
138138
deps = [
139139
":action_lookup_data",
140140
":action_lookup_key",
141-
":arg_chunk",
142141
":artifact_owner",
143142
":artifacts",
144143
":commandline_item",
@@ -168,8 +167,6 @@ java_library(
168167
"//src/main/java/com/google/devtools/build/lib/concurrent",
169168
"//src/main/java/com/google/devtools/build/lib/events",
170169
"//src/main/java/com/google/devtools/build/lib/packages",
171-
"//src/main/java/com/google/devtools/build/lib/profiler",
172-
"//src/main/java/com/google/devtools/build/lib/profiler:google-auto-profiler-utils",
173170
"//src/main/java/com/google/devtools/build/lib/remote:store",
174171
"//src/main/java/com/google/devtools/build/lib/shell",
175172
"//src/main/java/com/google/devtools/build/lib/skyframe:detailed_exceptions",
@@ -213,11 +210,6 @@ java_library(
213210
],
214211
)
215212

216-
java_library(
217-
name = "arg_chunk",
218-
srcs = ["ArgChunk.java"],
219-
)
220-
221213
java_library(
222214
name = "action_environment",
223215
srcs = ["ActionEnvironment.java"],
@@ -299,6 +291,7 @@ java_library(
299291
srcs = [
300292
"ActionInput.java",
301293
"ActionKeyContext.java",
294+
"ArgChunk.java",
302295
"Artifact.java",
303296
"ArtifactCodecs.java",
304297
"ArtifactFactory.java",
@@ -312,7 +305,6 @@ java_library(
312305
deps = [
313306
":action_lookup_data",
314307
":action_lookup_key",
315-
":arg_chunk",
316308
":artifact_owner",
317309
":commandline_item",
318310
":package_roots",

src/main/java/com/google/devtools/build/lib/actions/CommandLine.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ public SimpleArgChunk(Iterable<String> args) {
5757
}
5858

5959
@Override
60-
public Iterable<String> arguments() {
60+
public Iterable<String> arguments(PathMapper pathMapper) {
6161
return args;
6262
}
6363

6464
@Override
65-
public int totalArgLength() {
65+
public int totalArgLength(PathMapper pathMapper) {
6666
int total = 0;
6767
for (String arg : args) {
6868
total += arg.length() + 1;

src/main/java/com/google/devtools/build/lib/actions/CommandLines.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,14 @@ ExpandedCommandLines expand(
105105
ParamFileInfo paramFileInfo = pair.paramFileInfo;
106106
ArgChunk chunk = commandLine.expand(inputMetadataProvider, pathMapper);
107107
if (paramFileInfo == null) {
108-
arguments.addAll(chunk.arguments());
109-
cmdLineLength += chunk.totalArgLength();
108+
arguments.addAll(chunk.arguments(pathMapper));
109+
cmdLineLength += chunk.totalArgLength(pathMapper);
110110
} else {
111111
boolean useParamFile = true;
112112
if (!paramFileInfo.always()) {
113-
int tentativeCmdLineLength = cmdLineLength + chunk.totalArgLength();
113+
int tentativeCmdLineLength = cmdLineLength + chunk.totalArgLength(pathMapper);
114114
if (tentativeCmdLineLength <= conservativeMaxLength) {
115-
arguments.addAll(chunk.arguments());
115+
arguments.addAll(chunk.arguments(pathMapper));
116116
cmdLineLength = tentativeCmdLineLength;
117117
useParamFile = false;
118118
}
@@ -135,16 +135,16 @@ ExpandedCommandLines expand(
135135
paramFiles.add(
136136
new ParamFileActionInput(
137137
paramFileExecPath,
138-
ParameterFile.flagsOnly(chunk.arguments()),
138+
ParameterFile.flagsOnly(chunk.arguments(pathMapper)),
139139
paramFileInfo.getFileType()));
140-
for (String positionalArg : ParameterFile.nonFlags(chunk.arguments())) {
140+
for (String positionalArg : ParameterFile.nonFlags(chunk.arguments(pathMapper))) {
141141
arguments.add(positionalArg);
142142
cmdLineLength += positionalArg.length() + 1;
143143
}
144144
} else {
145145
paramFiles.add(
146146
new ParamFileActionInput(
147-
paramFileExecPath, chunk.arguments(), paramFileInfo.getFileType()));
147+
paramFileExecPath, chunk.arguments(pathMapper), paramFileInfo.getFileType()));
148148
}
149149
}
150150
}

src/main/java/com/google/devtools/build/lib/actions/PathMapper.java

+22
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package com.google.devtools.build.lib.actions;
1616

1717
import com.google.devtools.build.docgen.annot.DocCategory;
18+
import com.google.devtools.build.lib.actions.Artifact.DerivedArtifact;
1819
import com.google.devtools.build.lib.actions.CommandLineItem.ExceptionlessMapFn;
1920
import com.google.devtools.build.lib.actions.CommandLineItem.MapFn;
2021
import com.google.devtools.build.lib.analysis.config.CoreOptions;
@@ -105,6 +106,17 @@ default String getMappedExecPathString(ActionInput artifact) {
105106
return map(artifact.getExecPath()).getPathString();
106107
}
107108

109+
/**
110+
* Returns the difference {@code artifact.getExecPathString().length() -
111+
* getMappedExecPathString(artifact).length()}, i.e., the unmapped path length minus the mapped
112+
* path length.
113+
*
114+
* <p>Implementations should provide a more efficient implementation that avoids allocations.
115+
*/
116+
default int computeExecPathLengthDiff(DerivedArtifact artifact) {
117+
return artifact.getExecPathString().length() - getMappedExecPathString(artifact).length();
118+
}
119+
108120
/**
109121
* We don't yet have a Starlark API for mapping paths in command lines. Simple Starlark calls like
110122
* {@code args.add(arg_name, file_path} are automatically handled. But calls that involve custom
@@ -193,6 +205,16 @@ public PathFragment map(PathFragment execPath) {
193205
return execPath;
194206
}
195207

208+
@Override
209+
public String getMappedExecPathString(ActionInput artifact) {
210+
return artifact.getExecPathString();
211+
}
212+
213+
@Override
214+
public int computeExecPathLengthDiff(DerivedArtifact artifact) {
215+
return 0;
216+
}
217+
196218
@Override
197219
public FileRootApi mapRoot(Artifact artifact) {
198220
return artifact.getRoot();

src/main/java/com/google/devtools/build/lib/analysis/BUILD

-3
Original file line numberDiff line numberDiff line change
@@ -1329,7 +1329,6 @@ java_library(
13291329
deps = [
13301330
":actions/abstract_file_write_action",
13311331
"//src/main/java/com/google/devtools/build/lib/actions",
1332-
"//src/main/java/com/google/devtools/build/lib/actions:arg_chunk",
13331332
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
13341333
"//src/main/java/com/google/devtools/build/lib/actions:commandline_item",
13351334
"//src/main/java/com/google/devtools/build/lib/analysis/config:core_options",
@@ -1353,7 +1352,6 @@ java_library(
13531352
deps = [
13541353
"//src/main/java/com/google/devtools/build/lib/actions",
13551354
"//src/main/java/com/google/devtools/build/lib/actions:action_input_helper",
1356-
"//src/main/java/com/google/devtools/build/lib/actions:arg_chunk",
13571355
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
13581356
"//src/main/java/com/google/devtools/build/lib/actions:commandline_item",
13591357
"//src/main/java/com/google/devtools/build/lib/actions:execution_requirements",
@@ -1759,7 +1757,6 @@ java_library(
17591757
srcs = ["starlark/StarlarkCustomCommandLine.java"],
17601758
deps = [
17611759
"//src/main/java/com/google/devtools/build/lib/actions",
1762-
"//src/main/java/com/google/devtools/build/lib/actions:arg_chunk",
17631760
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
17641761
"//src/main/java/com/google/devtools/build/lib/actions:commandline_item",
17651762
"//src/main/java/com/google/devtools/build/lib/actions:fileset_output_symlink",

src/main/java/com/google/devtools/build/lib/analysis/actions/ParameterFileWriteAction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ private static class ParamFileWriter implements DeterministicWriter {
167167

168168
@Override
169169
public void writeTo(OutputStream out) throws IOException {
170-
ParameterFile.writeParameterFile(out, arguments.arguments(), type);
170+
ParameterFile.writeParameterFile(out, arguments.arguments(PathMapper.NOOP), type);
171171
}
172172
}
173173

src/main/java/com/google/devtools/build/lib/analysis/actions/StrippingPathMapper.java

+20-13
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
import com.google.devtools.build.lib.actions.CommandLineItem;
2828
import com.google.devtools.build.lib.actions.CommandLineItem.ExceptionlessMapFn;
2929
import com.google.devtools.build.lib.actions.CommandLineItem.MapFn;
30-
import com.google.devtools.build.lib.actions.CommandLines.ParamFileActionInput;
3130
import com.google.devtools.build.lib.actions.PathMapper;
3231
import com.google.devtools.build.lib.actions.Spawn;
32+
import com.google.devtools.build.lib.actions.cache.VirtualActionInput;
3333
import com.google.devtools.build.lib.starlarkbuildapi.FileRootApi;
3434
import com.google.devtools.build.lib.vfs.PathFragment;
3535
import java.util.HashMap;
@@ -145,7 +145,7 @@ static Optional<PathMapper> tryCreate(AbstractAction action, boolean isStarlarkA
145145

146146
@Override
147147
public String getMappedExecPathString(ActionInput artifact) {
148-
if (isSupportedInputType(artifact) && isOutputPath(artifact, outputRoot)) {
148+
if (isSupported(artifact)) {
149149
return strip(artifact.getExecPath()).getPathString();
150150
} else {
151151
return artifact.getExecPathString();
@@ -157,6 +157,15 @@ public PathFragment map(PathFragment execPath) {
157157
return isOutputPath(execPath, outputRoot) ? strip(execPath) : execPath;
158158
}
159159

160+
@Override
161+
public int computeExecPathLengthDiff(DerivedArtifact artifact) {
162+
String unmappedPath = artifact.getExecPathString();
163+
// bazel-out/k8-fastbuild/... is mapped to bazel-out/${FIXED_CONFIG_SEGMENT}/...
164+
int firstSlash = outputRoot.getPathString().length() + 1;
165+
int secondSlash = unmappedPath.indexOf('/', firstSlash + 1);
166+
return (secondSlash - firstSlash) - FIXED_CONFIG_SEGMENT.length();
167+
}
168+
160169
@Override
161170
public ArgChunk mapCustomStarlarkArgs(ArgChunk chunk) {
162171
if (!isStarlarkAction) {
@@ -174,7 +183,7 @@ public ArgChunk mapCustomStarlarkArgs(ArgChunk chunk) {
174183

175184
// TODO: b/327187486 - This materializes strings when totalArgLength() is called. Can it
176185
// compute the total arg length without creating garbage strings?
177-
Iterable<String> args = chunk.arguments();
186+
Iterable<String> args = chunk.arguments(this);
178187
return new SimpleArgChunk(() -> new CustomStarlarkArgsIterator(args.iterator(), argStripper));
179188
}
180189

@@ -204,10 +213,14 @@ public FileRootApi mapRoot(Artifact artifact) {
204213
return PathMapper.super.mapRoot(artifact);
205214
}
206215

207-
private boolean isSupportedInputType(ActionInput artifact) {
208-
return artifact instanceof DerivedArtifact
209-
|| artifact instanceof ParamFileActionInput
210-
|| artifact instanceof BasicActionInput;
216+
private boolean isSupported(ActionInput artifact) {
217+
if (artifact instanceof DerivedArtifact) {
218+
return true;
219+
}
220+
if (artifact instanceof BasicActionInput || artifact instanceof VirtualActionInput) {
221+
return isOutputPath(artifact, outputRoot);
222+
}
223+
return false;
211224
}
212225

213226
private static final class CustomStarlarkArgsIterator implements Iterator<String> {
@@ -339,12 +352,6 @@ private static boolean isPathStrippable(
339352
* Strips the configuration prefix from an output artifact's exec path.
340353
*/
341354
private static PathFragment strip(PathFragment execPath) {
342-
if (execPath.subFragment(1, 2).getPathString().equals("tmp")) {
343-
return execPath
344-
.subFragment(0, 2)
345-
.getRelative(FIXED_CONFIG_SEGMENT)
346-
.getRelative(execPath.subFragment(3));
347-
}
348355
return execPath
349356
.subFragment(0, 1)
350357
// Keep the config segment, but replace it with a fixed string to improve cacheability while

0 commit comments

Comments
 (0)