Skip to content

Commit a80cd2f

Browse files
committed
Implement global index store cache
bazelbuild#567 swift and clang write index data based on the status of what resides in `index-store-path`. When using a transient per-swift-library index, it writes O( M imports * N libs) indexer data and slows down compilation significantly: to the tune of 300% slow down and 6GB+ index data in my testing. Bazel likes to use per `swift_library` data in order to remote cache them, which needs extra consideration to work with the design of swift and clang and preserve the performance This PR does 2 things to make index while building use the original model of the index-store so we don't have to patch clang and swift for this yet. When the flag `-Xwrapped-swift-enable-global-index-store` is set: 1. it writes to a global index cache, `bazel-out/global_index_store` 2. it copies indexstore data (records and units) to where Bazel expected them for caching: e.g. the original location with `swift.index_while_building` Note that while this uses a `index-import` binary, it is very different than how index-import currently works. In the variant this expects, it is program that can _copy_ index data for specific compiler outputs. This has the effect fo remote caching the subset that was used in compilation for this library. I added a detailed description of this feature in the RFC to add it there MobileNativeFoundation/index-import#53. In practice, transitive imports will be indexed by deps and if they aren't cached then the fallback is Xcode wil index transitive files if we set those as deps on the unit files
1 parent cde2112 commit a80cd2f

File tree

4 files changed

+122
-4
lines changed

4 files changed

+122
-4
lines changed

swift/internal/compiling.bzl

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ load(
4747
"SWIFT_FEATURE_GLOBAL_MODULE_CACHE_USES_TMPDIR",
4848
"SWIFT_FEATURE_INDEX_WHILE_BUILDING",
4949
"SWIFT_FEATURE_LAYERING_CHECK",
50+
"SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2",
5051
"SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD",
5152
"SWIFT_FEATURE_NO_EMBED_DEBUG_MODULE",
5253
"SWIFT_FEATURE_NO_GENERATED_MODULE_MAP",
@@ -812,6 +813,7 @@ def compile_action_configs(
812813
configurators = [_index_while_building_configurator],
813814
features = [SWIFT_FEATURE_INDEX_WHILE_BUILDING],
814815
),
816+
815817
swift_toolchain_config.action_config(
816818
actions = [swift_action_names.COMPILE],
817819
configurators = [
@@ -824,6 +826,11 @@ def compile_action_configs(
824826
SWIFT_FEATURE_DISABLE_SYSTEM_INDEX,
825827
],
826828
),
829+
swift_toolchain_config.action_config(
830+
actions = [swift_action_names.COMPILE],
831+
configurators = [_index_while_building_configurator],
832+
features = [SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2],
833+
),
827834

828835
# User-defined conditional compilation flags (defined for Swift; those
829836
# passed directly to ClangImporter are handled above).
@@ -1742,6 +1749,15 @@ def compile(
17421749
else:
17431750
vfsoverlay_file = None
17441751

1752+
# For index while building v2 it uses a global index store which is
1753+
# configured by this flag
1754+
additional_copts = []
1755+
if is_feature_enabled(
1756+
feature_configuration = feature_configuration,
1757+
feature_name = SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2,
1758+
):
1759+
additional_copts.append("-Xwrapped-swift=-enable-global-index-store")
1760+
17451761
prerequisites = struct(
17461762
additional_inputs = additional_inputs,
17471763
bin_dir = bin_dir,
@@ -1757,7 +1773,7 @@ def compile(
17571773
source_files = srcs,
17581774
transitive_modules = transitive_modules,
17591775
transitive_swiftmodules = transitive_swiftmodules,
1760-
user_compile_flags = copts,
1776+
user_compile_flags = copts + additional_copts,
17611777
vfsoverlay_file = vfsoverlay_file,
17621778
vfsoverlay_search_path = _SWIFTMODULES_VFS_ROOT,
17631779
workspace_name = workspace_name,
@@ -2171,10 +2187,19 @@ def _declare_compile_outputs(
21712187
# Configure index-while-building if requested. IDEs and other indexing tools
21722188
# can enable this feature on the command line during a build and then access
21732189
# the index store artifacts that are produced.
2174-
index_while_building = is_feature_enabled(
2190+
index_while_building_v1 = is_feature_enabled(
21752191
feature_configuration = feature_configuration,
21762192
feature_name = SWIFT_FEATURE_INDEX_WHILE_BUILDING,
21772193
)
2194+
index_while_building_v2 = is_feature_enabled(
2195+
feature_configuration = feature_configuration,
2196+
feature_name = SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2,
2197+
)
2198+
2199+
if index_while_building_v1 and index_while_building_v2:
2200+
fail("can't use both swift.index_while_building and swift.index_while_building_v2")
2201+
2202+
index_while_building = (index_while_building_v1 or index_while_building_v2)
21782203
if (
21792204
index_while_building and
21802205
not _index_store_path_overridden(user_compile_flags)

swift/internal/feature_names.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ SWIFT_FEATURE_ENABLE_TESTING = "swift.enable_testing"
9898
# warnings otherwise.
9999
SWIFT_FEATURE_FULL_DEBUG_INFO = "swift.full_debug_info"
100100

101+
# Index while building - using a global index store cache
102+
# https://docs.google.com/document/d/1cH2sTpgSnJZCkZtJl1aY-rzy4uGPcrI-6RrUpdATO2Q/
103+
SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2 = "swift.index_while_building_v2"
104+
101105
# If enabled, the compilation action for a target will produce an index store.
102106
SWIFT_FEATURE_INDEX_WHILE_BUILDING = "swift.index_while_building"
103107

tools/worker/BUILD

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ config_setting(
1818
},
1919
)
2020

21+
# Internal hinge for index while building V2 feature
22+
config_setting(
23+
name = "index_while_building_v2",
24+
values = {
25+
"features": "swift.index_while_building_v2"
26+
},
27+
)
28+
2129
cc_library(
2230
name = "compile_with_worker",
2331
srcs = [
@@ -37,6 +45,18 @@ cc_library(
3745
"@com_github_nlohmann_json//:json",
3846
"@com_google_protobuf//:protobuf",
3947
],
48+
copts = select({
49+
":index_while_building_v2": [
50+
"-DINDEX_IMPORT_PATH=\\\"$(rootpath @build_bazel_rules_swift_index_import//:index_import)\\\""
51+
],
52+
"//conditions:default": [],
53+
}),
54+
data = select({
55+
":index_while_building_v2": [
56+
"@build_bazel_rules_swift_index_import//:index_import"
57+
],
58+
"//conditions:default": [],
59+
})
4060
)
4161

4262
cc_library(

tools/worker/work_processor.cc

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@
2222
#include <nlohmann/json.hpp>
2323
#include <sstream>
2424
#include <string>
25+
#include <vector>
2526

2627
#include "tools/common/file_system.h"
2728
#include "tools/common/path_utils.h"
29+
#include "tools/common/process.h"
30+
#include "tools/common/string_utils.h"
2831
#include "tools/common/temp_file.h"
2932
#include "tools/worker/output_file_map.h"
3033
#include "tools/worker/swift_runner.h"
@@ -63,6 +66,11 @@ void WorkProcessor::ProcessWorkRequest(
6366
bool is_wmo = false;
6467

6568
std::string prev_arg;
69+
70+
std::string bazel_index_store_path;
71+
bool enable_global_index_store = false;
72+
73+
6674
for (auto arg : request.arguments()) {
6775
auto original_arg = arg;
6876
// Peel off the `-output-file-map` argument, so we can rewrite it if
@@ -76,16 +84,41 @@ void WorkProcessor::ProcessWorkRequest(
7684
is_wmo = true;
7785
}
7886

87+
// Peel off index-store-path, we will conditionally pop on a new one if
88+
// it passes -Xwrapped-swift-enable-global-index-store
89+
if (arg == "-index-store-path") {
90+
arg.clear();
91+
} else if (prev_arg == "-index-store-path") {
92+
bazel_index_store_path = arg;
93+
arg.clear();
94+
}
95+
if (arg == "-Xwrapped-swift=-enable-global-index-store") {
96+
arg.clear();
97+
enable_global_index_store = true;
98+
}
99+
79100
if (!arg.empty()) {
80101
params_file_stream << arg << '\n';
81102
}
82103

83104
prev_arg = original_arg;
84105
}
85106

107+
// Material within this index store is namespaced to arcitecture, os, etc
108+
auto exec_root = GetCurrentDirectory();
109+
auto global_index_store = exec_root + "/bazel-out/global_index_store";
110+
if (enable_global_index_store) {
111+
processed_args.push_back("-index-store-path");
112+
processed_args.push_back(global_index_store);
113+
} else if (!bazel_index_store_path.empty()) {
114+
processed_args.push_back("-index-store-path");
115+
processed_args.push_back(bazel_index_store_path);
116+
}
117+
86118
if (!output_file_map_path.empty()) {
119+
output_file_map.ReadFromPath(output_file_map_path);
120+
87121
if (!is_wmo) {
88-
output_file_map.ReadFromPath(output_file_map_path);
89122

90123
// Rewrite the output file map to use the incremental storage area and
91124
// pass the compiler the path to the rewritten file.
@@ -129,7 +162,6 @@ void WorkProcessor::ProcessWorkRequest(
129162
SwiftRunner swift_runner(processed_args, /*force_response_file=*/true);
130163

131164
int exit_code = swift_runner.Run(&stderr_stream, /*stdout_to_stderr=*/true);
132-
133165
if (!is_wmo) {
134166
// Copy the output files from the incremental storage area back to the
135167
// locations where Bazel declared the files.
@@ -143,6 +175,43 @@ void WorkProcessor::ProcessWorkRequest(
143175
}
144176
}
145177

178+
// Import global indexes if compilation succeeds
179+
if (exit_code != EXIT_FAILURE && enable_global_index_store) {
180+
std::vector<std::string> ii_args;
181+
182+
// The index-import runfile path is pased as a define
183+
#if defined(INDEX_IMPORT_PATH)
184+
ii_args.push_back(INDEX_IMPORT_PATH);
185+
#else
186+
// Logical error
187+
std::cerr << "Incorrectly compiled work_processor.cc";
188+
exit_code = EXIT_FAILURE;
189+
#endif
190+
191+
// Use the output path map to deterine what compilation ouputs to import
192+
auto outputs = output_file_map.incremental_outputs();
193+
std::map<std::string, std::string>::iterator it;
194+
for (it = outputs.begin(); it != outputs.end(); it++) {
195+
// Need the actual output paths of the compiler - not bazel
196+
auto output_path = is_wmo ? it->first : it->second;
197+
198+
auto file_type = output_path.substr(output_path.find_last_of(".") + 1);
199+
if (file_type == "o") {
200+
ii_args.push_back("-import-output-file");
201+
ii_args.push_back(output_path);
202+
}
203+
}
204+
205+
// Copy back from the global index store to bazel's index store
206+
ii_args.push_back(global_index_store);
207+
208+
ii_args.push_back(exec_root + "/" + bazel_index_store_path);
209+
210+
const std::vector<std::string>& ii_args_ = ii_args;
211+
// Dupes the output into the action for failure cases
212+
exit_code = RunSubProcess(ii_args_, &stderr_stream, /*stdout_to_stderr=*/true);
213+
}
214+
146215
response->set_exit_code(exit_code);
147216
response->set_output(stderr_stream.str());
148217
response->set_request_id(request.request_id());

0 commit comments

Comments
 (0)