Skip to content

Commit 39b450c

Browse files
Googlercopybara-github
authored andcommitted
Add diff_against_dynamic_baseline option to experimental_output_directory_naming_scheme.
To support, a new SkyFunction is introduced, BaselineOptionsFunction, with corresponding SkyValue and SkyKey. Also, a new function ExecutionTransitionFactory.createTransition was added to immediately create an ExecutionTransition. When --experimental_output_directory_naming_scheme=diff_against_dynamic_baseline, the behavior is similar to diff_against_baseline, except when "is Exec" is true (that is, an ExecutionTransition has previously been applied), the baseline is chosen to be result of the ExecutionTransition being applied to the normal baseline. This is meant to resolve an issue with performance when using remote execution engines that collate requests from multiple users. They were seeing varying output paths for actions in an execution configuration depending on how much 'resetting' the exec transition was doing because of varying user commandlines. This relates to #14023 and #18002 PiperOrigin-RevId: 537097551 Change-Id: I72966406b7b8af0174a81b638bf0d1fb62466653
1 parent b9e1ec7 commit 39b450c

File tree

11 files changed

+338
-26
lines changed

11 files changed

+338
-26
lines changed

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,21 @@ java_library(
13521352
],
13531353
)
13541354

1355+
java_library(
1356+
name = "config/transitions/baseline_options_value",
1357+
srcs = [
1358+
"config/transitions/BaselineOptionsValue.java",
1359+
],
1360+
deps = [
1361+
":config/build_options",
1362+
"//src/main/java/com/google/devtools/build/lib/concurrent:thread_safety",
1363+
"//src/main/java/com/google/devtools/build/lib/skyframe:sky_functions",
1364+
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
1365+
"//third_party:auto_value",
1366+
"//third_party:error_prone_annotations",
1367+
],
1368+
)
1369+
13551370
# TODO(b/144899336): This should be analysis/actions/BUILD
13561371
java_library(
13571372
name = "actions/abstract_file_write_action",

src/main/java/com/google/devtools/build/lib/analysis/config/CoreOptions.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,9 @@ public enum OutputDirectoryNamingScheme {
563563
/** Use `affected by starlark transition` to track configuration changes */
564564
LEGACY,
565565
/** Produce name based on diff from some baseline BuildOptions (usually top-level) */
566-
DIFF_AGAINST_BASELINE
566+
DIFF_AGAINST_BASELINE,
567+
/** Like DIFF_AGAINST_BASELINE, but compare against post-exec baseline if isExec is set. */
568+
DIFF_AGAINST_DYNAMIC_BASELINE
567569
}
568570

569571
/** Converter for the {@code --experimental_output_directory_naming_scheme} options. */
@@ -589,6 +591,17 @@ public OutputDirectoryNamingSchemeConverter() {
589591
+ " for all configuration, by diffing against the top-level configuration.")
590592
public OutputDirectoryNamingScheme outputDirectoryNamingScheme;
591593

594+
public boolean useBaselineForOutputDirectoryNamingScheme() {
595+
switch (outputDirectoryNamingScheme) {
596+
case DIFF_AGAINST_BASELINE:
597+
case DIFF_AGAINST_DYNAMIC_BASELINE:
598+
return true;
599+
case LEGACY:
600+
return false;
601+
}
602+
throw new IllegalStateException("unreachable");
603+
}
604+
592605
@Option(
593606
name = "is exec configuration",
594607
defaultValue = "false",

src/main/java/com/google/devtools/build/lib/analysis/config/ExecutionTransitionFactory.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@
3636
* {@link TransitionFactory} implementation which creates a {@link PatchTransition} which will
3737
* transition to a configuration suitable for building dependencies for the execution platform of
3838
* the depending target.
39+
*
40+
* <p>Note that execGroup is not directly consumed by the involved transition but instead stored
41+
* here. Instead, the rule definition stores it in this factory. Then, toolchain resolution extracts
42+
* and consumes it to store an execution platform in attrs. Finally, the execution platform is read
43+
* by the factory to create the transition.
3944
*/
4045
public class ExecutionTransitionFactory
4146
implements TransitionFactory<AttributeTransitionData>, ExecTransitionFactoryApi {
@@ -48,6 +53,11 @@ public static ExecutionTransitionFactory createFactory() {
4853
return new ExecutionTransitionFactory(DEFAULT_EXEC_GROUP_NAME);
4954
}
5055

56+
/** Returns a new {@link ExecutionTransition} immediately. */
57+
public static PatchTransition createTransition(@Nullable Label executionPlatform) {
58+
return new ExecutionTransition(executionPlatform);
59+
}
60+
5161
/**
5262
* Returns a new {@link ExecutionTransitionFactory} for the given {@link
5363
* com.google.devtools.build.lib.packages.ExecGroup}.
@@ -104,6 +114,10 @@ public String getName() {
104114

105115
@Override
106116
public ImmutableSet<Class<? extends FragmentOptions>> requiresOptionFragments() {
117+
// This is technically a lie since the call to underlying().createExecOptions is transitively
118+
// reading and potentially modifying all fragments. There is currently no way for the
119+
// transition to actually list all fragments like this and thus only lists the ones that are
120+
// directly being read here. Note that this transition is exceptional in its implementation.
107121
return FRAGMENTS;
108122
}
109123

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2023 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.devtools.build.lib.analysis.config.transitions;
16+
17+
import com.google.auto.value.AutoValue;
18+
import com.google.devtools.build.lib.analysis.config.BuildOptions;
19+
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
20+
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
21+
import com.google.devtools.build.lib.skyframe.SkyFunctions;
22+
import com.google.devtools.build.skyframe.SkyFunctionName;
23+
import com.google.devtools.build.skyframe.SkyKey;
24+
import com.google.devtools.build.skyframe.SkyValue;
25+
import com.google.errorprone.annotations.CheckReturnValue;
26+
27+
/**
28+
* This contains the baseline options to compare against when constructing output paths.
29+
*
30+
* <p>When constructing the output mnemonic as part of making a {@link BuildConfigurationValue} and
31+
* the selected naming scheme is to diff against a baseline, this function returns the baseline to
32+
* use for that comparison. Differences in options between the given option and this baseline will
33+
* then be used to append a deconflicting ST-hash to the output mnemonic.
34+
*
35+
* <p>The afterExecTransition option in the key will apply the exec transition to the usual
36+
* baseline. It is expected that this is set whenever the given options have isExec set (and thus an
37+
* exec transition has already been applied to those options). The expectation here is that, as the
38+
* exec transition particularly sets many options, comparing against a post-exec baseline will yield
39+
* fewer diffenences. Note that some indicator must be added to the mnemonic (e.g. -exec-) in order
40+
* to deconflict for similar options where isExec is not set.
41+
*/
42+
@CheckReturnValue
43+
@Immutable
44+
@ThreadSafe
45+
@AutoValue
46+
public abstract class BaselineOptionsValue implements SkyValue {
47+
public abstract BuildOptions toOptions();
48+
49+
public static BaselineOptionsValue create(BuildOptions toOptions) {
50+
return new AutoValue_BaselineOptionsValue(toOptions);
51+
}
52+
53+
public static Key key(boolean afterExecTransition) {
54+
return Key.create(afterExecTransition);
55+
}
56+
57+
/** {@link SkyKey} implementation used for {@link BaselineOptionsValue}. */
58+
@CheckReturnValue
59+
@Immutable
60+
@ThreadSafe
61+
@AutoValue
62+
public abstract static class Key implements SkyKey {
63+
public abstract boolean afterExecTransition();
64+
65+
@Override
66+
public SkyFunctionName functionName() {
67+
return SkyFunctions.BASELINE_OPTIONS;
68+
}
69+
70+
static Key create(boolean afterExecTransition) {
71+
return new AutoValue_BaselineOptionsValue_Key(afterExecTransition);
72+
}
73+
}
74+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ java_library(
2929
"ActionOutputDirectoryHelper.java",
3030
"AspectCompletor.java",
3131
"AspectFunction.java",
32+
"BaselineOptionsFunction.java",
3233
"BazelSkyframeExecutorConstants.java",
3334
"BuildConfigurationFunction.java",
3435
"BuildInfoCollectionFunction.java",
@@ -234,13 +235,15 @@ java_library(
234235
"//src/main/java/com/google/devtools/build/lib/analysis:config/config_conditions",
235236
"//src/main/java/com/google/devtools/build/lib/analysis:config/config_matching_provider",
236237
"//src/main/java/com/google/devtools/build/lib/analysis:config/core_options",
238+
"//src/main/java/com/google/devtools/build/lib/analysis:config/execution_transition_factory",
237239
"//src/main/java/com/google/devtools/build/lib/analysis:config/fragment_factory",
238240
"//src/main/java/com/google/devtools/build/lib/analysis:config/fragment_options",
239241
"//src/main/java/com/google/devtools/build/lib/analysis:config/invalid_configuration_exception",
240242
"//src/main/java/com/google/devtools/build/lib/analysis:config/optioninfo",
241243
"//src/main/java/com/google/devtools/build/lib/analysis:config/starlark_defined_config_transition",
242244
"//src/main/java/com/google/devtools/build/lib/analysis:config/starlark_transition_cache",
243245
"//src/main/java/com/google/devtools/build/lib/analysis:config/toolchain_type_requirement",
246+
"//src/main/java/com/google/devtools/build/lib/analysis:config/transitions/baseline_options_value",
244247
"//src/main/java/com/google/devtools/build/lib/analysis:config/transitions/configuration_transition",
245248
"//src/main/java/com/google/devtools/build/lib/analysis:config/transitions/no_transition",
246249
"//src/main/java/com/google/devtools/build/lib/analysis:config/transitions/null_transition",
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright 2023 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package com.google.devtools.build.lib.skyframe;
15+
16+
import com.google.devtools.build.lib.analysis.PlatformOptions;
17+
import com.google.devtools.build.lib.analysis.config.BuildOptions;
18+
import com.google.devtools.build.lib.analysis.config.ExecutionTransitionFactory;
19+
import com.google.devtools.build.lib.analysis.config.transitions.BaselineOptionsValue;
20+
import com.google.devtools.build.lib.analysis.config.transitions.PatchTransition;
21+
import com.google.devtools.build.lib.analysis.config.transitions.TransitionUtil;
22+
import com.google.devtools.build.lib.cmdline.Label;
23+
import com.google.devtools.build.skyframe.SkyFunction;
24+
import com.google.devtools.build.skyframe.SkyFunctionException;
25+
import com.google.devtools.build.skyframe.SkyKey;
26+
import com.google.devtools.build.skyframe.SkyValue;
27+
import com.google.devtools.common.options.OptionsParsingException;
28+
import javax.annotation.Nullable;
29+
30+
/** A builder for {@link BaselineOptionsValue} instances. */
31+
public final class BaselineOptionsFunction implements SkyFunction {
32+
@Override
33+
@Nullable
34+
public SkyValue compute(SkyKey skyKey, Environment env)
35+
throws InterruptedException, BaselineOptionsFunctionException {
36+
BaselineOptionsValue.Key key = (BaselineOptionsValue.Key) skyKey.argument();
37+
38+
BuildOptions rawBaselineOptions = PrecomputedValue.BASELINE_CONFIGURATION.get(env);
39+
40+
// Some test infrastructure only creates mock or partial top-level BuildOptions such that
41+
// PlatformOptions or even CoreOptions might not be included.
42+
// In that case, is not worth doing any special processing of the baseline.
43+
if (rawBaselineOptions.hasNoConfig()) {
44+
return BaselineOptionsValue.create(rawBaselineOptions);
45+
}
46+
47+
// Herein lies a hack to apply platform mappings to the baseline options.
48+
// TODO(blaze-configurability-team): this should become unnecessary once --platforms is marked
49+
// as EXPLICIT_IN_OUTPUT_PATH
50+
PlatformMappingValue platformMappingValue =
51+
(PlatformMappingValue)
52+
env.getValue(
53+
PlatformMappingValue.Key.create(
54+
rawBaselineOptions.get(PlatformOptions.class).platformMappings));
55+
if (platformMappingValue == null) {
56+
return null;
57+
}
58+
try {
59+
BuildOptions mappedBaselineOptions =
60+
BuildConfigurationKey.withPlatformMapping(platformMappingValue, rawBaselineOptions)
61+
.getOptions();
62+
63+
if (key.afterExecTransition()) {
64+
// A null executionPlatform actually skips transition application so need some value here.
65+
// It is safe to supply some fake value here (as long as it is constant) since the baseline
66+
// should never be used to actually construct an action or do toolchain resolution
67+
// TODO(twigg): This can eventually be replaced by the actual exec platform once
68+
// platforms is explicitly in the output path (with the garbage value as a fallback).
69+
PatchTransition execTransition =
70+
ExecutionTransitionFactory.createTransition(
71+
Label.parseCanonicalUnchecked(
72+
"//this_is_a_faked_exec_platform_for_blaze_internals"));
73+
BuildOptions toOptions =
74+
execTransition.patch(
75+
TransitionUtil.restrict(execTransition, mappedBaselineOptions), env.getListener());
76+
return BaselineOptionsValue.create(toOptions);
77+
} else {
78+
return BaselineOptionsValue.create(mappedBaselineOptions);
79+
}
80+
} catch (OptionsParsingException e) {
81+
throw new BaselineOptionsFunctionException(e);
82+
}
83+
}
84+
85+
private static final class BaselineOptionsFunctionException extends SkyFunctionException {
86+
BaselineOptionsFunctionException(Exception e) {
87+
super(e, Transience.PERSISTENT);
88+
}
89+
}
90+
}

src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationFunction.java

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@
2323
import com.google.common.collect.ImmutableSet;
2424
import com.google.devtools.build.lib.analysis.BlazeDirectories;
2525
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
26-
import com.google.devtools.build.lib.analysis.PlatformOptions;
2726
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
2827
import com.google.devtools.build.lib.analysis.config.BuildOptions;
2928
import com.google.devtools.build.lib.analysis.config.CoreOptions;
3029
import com.google.devtools.build.lib.analysis.config.FragmentFactory;
3130
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
3231
import com.google.devtools.build.lib.analysis.config.OptionInfo;
32+
import com.google.devtools.build.lib.analysis.config.transitions.BaselineOptionsValue;
3333
import com.google.devtools.build.lib.cmdline.Label;
3434
import com.google.devtools.build.lib.cmdline.RepositoryName;
3535
import com.google.devtools.build.lib.packages.RuleClassProvider;
@@ -41,7 +41,6 @@
4141
import com.google.devtools.build.skyframe.SkyValue;
4242
import com.google.devtools.common.options.OptionDefinition;
4343
import com.google.devtools.common.options.OptionMetadataTag;
44-
import com.google.devtools.common.options.OptionsParsingException;
4544
import java.util.Map;
4645
import java.util.TreeMap;
4746
import javax.annotation.Nullable;
@@ -81,35 +80,25 @@ public SkyValue compute(SkyKey skyKey, Environment env)
8180
BuildConfigurationKey key = (BuildConfigurationKey) skyKey.argument();
8281

8382
BuildOptions targetOptions = key.getOptions();
83+
CoreOptions coreOptions = targetOptions.get(CoreOptions.class);
8484

8585
String transitionDirectoryNameFragment;
8686
if (targetOptions.hasNoConfig()) {
8787
transitionDirectoryNameFragment = "noconfig"; // See NoConfigTransition.
88-
} else if (targetOptions
89-
.get(CoreOptions.class)
90-
.outputDirectoryNamingScheme
91-
.equals(CoreOptions.OutputDirectoryNamingScheme.DIFF_AGAINST_BASELINE)) {
92-
// Herein lies a hack to apply platform mappings to the baseline options.
93-
// TODO(blaze-configurability-team): this should become unnecessary once --platforms is marked
94-
// as EXPLICIT_IN_OUTPUT_PATH
95-
PlatformMappingValue platformMappingValue =
96-
(PlatformMappingValue)
97-
env.getValue(
98-
PlatformMappingValue.Key.create(
99-
targetOptions.get(PlatformOptions.class).platformMappings));
100-
if (platformMappingValue == null) {
88+
} else if (coreOptions.useBaselineForOutputDirectoryNamingScheme()) {
89+
boolean applyExecTransitionToBaseline =
90+
coreOptions.outputDirectoryNamingScheme.equals(
91+
CoreOptions.OutputDirectoryNamingScheme.DIFF_AGAINST_DYNAMIC_BASELINE)
92+
&& coreOptions.isExec;
93+
var baselineOptionsValue =
94+
(BaselineOptionsValue)
95+
env.getValue(BaselineOptionsValue.key(applyExecTransitionToBaseline));
96+
if (baselineOptionsValue == null) {
10197
return null;
10298
}
103-
BuildOptions baselineOptions = PrecomputedValue.BASELINE_CONFIGURATION.get(env);
104-
try {
105-
BuildOptions mappedBaselineOptions =
106-
BuildConfigurationKey.withPlatformMapping(platformMappingValue, baselineOptions)
107-
.getOptions();
108-
transitionDirectoryNameFragment =
109-
computeNameFragmentWithDiff(targetOptions, mappedBaselineOptions);
110-
} catch (OptionsParsingException e) {
111-
throw new BuildConfigurationFunctionException(e);
112-
}
99+
100+
transitionDirectoryNameFragment =
101+
computeNameFragmentWithDiff(targetOptions, baselineOptionsValue.toOptions());
113102
} else {
114103
transitionDirectoryNameFragment =
115104
computeNameFragmentWithAffectedByStarlarkTransition(targetOptions);

src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ public final class SkyFunctions {
9494
static final SkyFunctionName TEST_COMPLETION = SkyFunctionName.createHermetic("TEST_COMPLETION");
9595
public static final SkyFunctionName BUILD_CONFIGURATION =
9696
SkyFunctionName.createHermetic("BUILD_CONFIGURATION");
97+
public static final SkyFunctionName BASELINE_OPTIONS =
98+
SkyFunctionName.createHermetic("BASELINE_OPTIONS");
9799
public static final SkyFunctionName STARLARK_BUILD_SETTINGS_DETAILS =
98100
SkyFunctionName.createHermetic("STARLARK_BUILD_SETTINGS_DETAILS");
99101
// Action execution can be nondeterministic, so semi-hermetic.

src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ private ImmutableMap<SkyFunctionName, SkyFunction> skyFunctions() {
665665
map.put(
666666
SkyFunctions.BUILD_CONFIGURATION,
667667
new BuildConfigurationFunction(directories, ruleClassProvider));
668+
map.put(SkyFunctions.BASELINE_OPTIONS, new BaselineOptionsFunction());
668669
map.put(
669670
SkyFunctions.STARLARK_BUILD_SETTINGS_DETAILS, new StarlarkBuildSettingsDetailsFunction());
670671
map.put(SkyFunctions.WORKSPACE_NAME, new WorkspaceNameFunction());

0 commit comments

Comments
 (0)