Skip to content

Commit 81a9600

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 aab5e21 commit 81a9600

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
@@ -46,6 +46,7 @@ load(
4646
"SWIFT_FEATURE_FULL_DEBUG_INFO",
4747
"SWIFT_FEATURE_INDEX_WHILE_BUILDING",
4848
"SWIFT_FEATURE_LAYERING_CHECK",
49+
"SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2",
4950
"SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD",
5051
"SWIFT_FEATURE_NO_EMBED_DEBUG_MODULE",
5152
"SWIFT_FEATURE_NO_GENERATED_MODULE_MAP",
@@ -781,6 +782,7 @@ def compile_action_configs(
781782
configurators = [_index_while_building_configurator],
782783
features = [SWIFT_FEATURE_INDEX_WHILE_BUILDING],
783784
),
785+
784786
swift_toolchain_config.action_config(
785787
actions = [swift_action_names.COMPILE],
786788
configurators = [
@@ -793,6 +795,11 @@ def compile_action_configs(
793795
SWIFT_FEATURE_DISABLE_SYSTEM_INDEX,
794796
],
795797
),
798+
swift_toolchain_config.action_config(
799+
actions = [swift_action_names.COMPILE],
800+
configurators = [_index_while_building_configurator],
801+
features = [SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2],
802+
),
796803

797804
# User-defined conditional compilation flags (defined for Swift; those
798805
# passed directly to ClangImporter are handled above).
@@ -1685,6 +1692,15 @@ def compile(
16851692
else:
16861693
vfsoverlay_file = None
16871694

1695+
# For index while building v2 it uses a global index store which is
1696+
# configured by this flag
1697+
additional_copts = []
1698+
if is_feature_enabled(
1699+
feature_configuration = feature_configuration,
1700+
feature_name = SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2,
1701+
):
1702+
additional_copts.append("-Xwrapped-swift=-enable-global-index-store")
1703+
16881704
prerequisites = struct(
16891705
additional_inputs = additional_inputs,
16901706
bin_dir = bin_dir,
@@ -1700,7 +1716,7 @@ def compile(
17001716
source_files = srcs,
17011717
transitive_modules = transitive_modules,
17021718
transitive_swiftmodules = transitive_swiftmodules,
1703-
user_compile_flags = copts,
1719+
user_compile_flags = copts + additional_copts,
17041720
vfsoverlay_file = vfsoverlay_file,
17051721
vfsoverlay_search_path = _SWIFTMODULES_VFS_ROOT,
17061722
# Merge the compile outputs into the prerequisites.
@@ -2027,10 +2043,19 @@ def _declare_compile_outputs(
20272043
# Configure index-while-building if requested. IDEs and other indexing tools
20282044
# can enable this feature on the command line during a build and then access
20292045
# the index store artifacts that are produced.
2030-
index_while_building = is_feature_enabled(
2046+
index_while_building_v1 = is_feature_enabled(
20312047
feature_configuration = feature_configuration,
20322048
feature_name = SWIFT_FEATURE_INDEX_WHILE_BUILDING,
20332049
)
2050+
index_while_building_v2 = is_feature_enabled(
2051+
feature_configuration = feature_configuration,
2052+
feature_name = SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2,
2053+
)
2054+
2055+
if index_while_building_v1 and index_while_building_v2:
2056+
fail("can't use both swift.index_while_building and swift.index_while_building_v2")
2057+
2058+
index_while_building = (index_while_building_v1 or index_while_building_v2)
20342059
if (
20352060
index_while_building and
20362061
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
@@ -90,6 +90,10 @@ SWIFT_FEATURE_ENABLE_TESTING = "swift.enable_testing"
9090
# warnings otherwise.
9191
SWIFT_FEATURE_FULL_DEBUG_INFO = "swift.full_debug_info"
9292

93+
# Index while building - using a global index store cache
94+
# https://docs.google.com/document/d/1cH2sTpgSnJZCkZtJl1aY-rzy4uGPcrI-6RrUpdATO2Q/
95+
SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2 = "swift.index_while_building_v2"
96+
9397
# If enabled, the compilation action for a target will produce an index store.
9498
SWIFT_FEATURE_INDEX_WHILE_BUILDING = "swift.index_while_building"
9599

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
}

0 commit comments

Comments
 (0)