Skip to content

Commit b332ef2

Browse files
authored
Improve build efficiency by building library subprojects in parallel (#31)
Each library variant build consists of three subprojects, which require sequential configuring and building. While the build steps can be parallelized by using multiple processes to compile the sources, the configuration step is largely single threaded. The multilib project has to build many variants now, and so a significant amount of time is spent waiting for the configuration of various projects to finish. This patch attempts to make the build process more efficient by running all the configuration steps for every variant subproject in parallel. The configuration/build steps in the underlying runtimes project are exposed, so that the higher level multilib project can invoke them as a single target. So instead of building each variant in turn, all the versions of compiler-rt are configured, then all versions are built, then all the C libraries are configured, and so on. This maximizes the amount of work that can be done at any given time. The build steps already benefit from parallelization: although built in series, each has the full number of available processes to use. However, at higher CPU counts there is an observable limit on how many processes each build can effectively use. So it may also be more efficient to instead run the build steps in parallel with a smaller number of proceses available to each. The option to control it (ENABLE_PARALLEL_LIB_BUILD) is OFF by default as which strategy is faster may come down to your available hardware, whereas ENABLE_PARALLEL_LIB_CONFIG should always be beneficial, so I've defaulted that to ON.
1 parent 750ee33 commit b332ef2

File tree

3 files changed

+132
-17
lines changed

3 files changed

+132
-17
lines changed

arm-software/embedded/CMakeLists.txt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,20 @@ option(
143143
be used to speed up repeated builds. This is not done by default,
144144
as it can also make the inital build slower due to the cold cache."
145145
)
146+
option(
147+
ENABLE_PARALLEL_LIB_CONFIG
148+
"Run the library variant configuration steps in parallel."
149+
ON
150+
)
151+
option(
152+
ENABLE_PARALLEL_LIB_BUILD
153+
"Run the library variant build steps in parallel."
154+
OFF
155+
)
156+
set(PARALLEL_LIB_BUILD_LEVELS
157+
"1" CACHE STRING
158+
"If ENABLE_PARALLEL_LIB_BUILD is ON, this number of processes will be assigned to each variant built."
159+
)
146160
option(
147161
ENABLE_QEMU_TESTING
148162
"Enable tests that use QEMU. This option is ON by default."
@@ -592,6 +606,9 @@ if(NOT PREBUILT_TARGET_LIBRARIES)
592606
-DLLVM_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/llvm
593607
-DMULTILIB_JSON=${LLVM_TOOLCHAIN_MULTILIB_JSON}
594608
-DENABLE_VARIANTS=${ENABLE_VARIANTS_PASSTHROUGH}
609+
-DENABLE_PARALLEL_LIB_CONFIG=${ENABLE_PARALLEL_LIB_CONFIG}
610+
-DENABLE_PARALLEL_LIB_BUILD=${ENABLE_PARALLEL_LIB_BUILD}
611+
-DPARALLEL_LIB_BUILD_LEVELS=${PARALLEL_LIB_BUILD_LEVELS}
595612
-DLIBC_HDRGEN=${LIBC_HDRGEN}
596613
-DFVP_INSTALL_DIR=${FVP_INSTALL_DIR}
597614
-DENABLE_QEMU_TESTING=${ENABLE_QEMU_TESTING}
@@ -600,9 +617,8 @@ if(NOT PREBUILT_TARGET_LIBRARIES)
600617
-DFETCHCONTENT_SOURCE_DIR_PICOLIBC=${FETCHCONTENT_SOURCE_DIR_PICOLIBC}
601618
-DFETCHCONTENT_SOURCE_DIR_NEWLIB=${FETCHCONTENT_SOURCE_DIR_NEWLIB}
602619
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
603-
USES_TERMINAL_CONFIGURE FALSE
620+
USES_TERMINAL_CONFIGURE TRUE
604621
USES_TERMINAL_BUILD TRUE
605-
USES_TERMINAL_TEST TRUE
606622
LIST_SEPARATOR ,
607623
CONFIGURE_HANDLED_BY_BUILD TRUE
608624
TEST_EXCLUDE_FROM_MAIN TRUE

arm-software/embedded/arm-multilib/CMakeLists.txt

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,25 @@ set(
5252
fvp/get_fvps.sh"
5353
)
5454
set(FVP_CONFIG_DIR "${TOOLCHAIN_SOURCE_DIR}/fvp/config" CACHE STRING "The directory in which the FVP models are installed.")
55+
option(
56+
ENABLE_PARALLEL_LIB_CONFIG
57+
"Run the library variant configuration steps in parallel."
58+
ON
59+
)
60+
option(
61+
ENABLE_PARALLEL_LIB_BUILD
62+
"Run the library variant build steps in parallel."
63+
OFF
64+
)
65+
set(PARALLEL_LIB_BUILD_LEVELS
66+
"1" CACHE STRING
67+
"If ENABLE_PARALLEL_LIB_BUILD is ON, this number of processes will be assigned to each variant built."
68+
)
69+
if(NOT CMAKE_GENERATOR MATCHES "Ninja")
70+
if (ENABLE_PARALLEL_LIB_CONFIG OR ENABLE_PARALLEL_LIB_BUILD)
71+
message(WARNING "Library build parallelization should only be enabled with the Ninja generator.")
72+
endif()
73+
endif()
5574

5675
# If a compiler launcher such as ccache has been set, it should be
5776
# passed down to each subproject build.
@@ -131,6 +150,34 @@ add_dependencies(
131150
check-unwind
132151
)
133152

153+
if(ENABLE_PARALLEL_LIB_CONFIG OR ENABLE_PARALLEL_LIB_BUILD)
154+
# Additional targets to build the variant subprojects in parallel.
155+
# The build steps can use multible jobs to compile in parallel, but
156+
# the configuration steps are largely single threaded. This creates a
157+
# bottleneck if each variant is built in series.
158+
# It is significantly faster to run all the subproject configuration
159+
# steps in parallel, run the build steps, then run the next set of
160+
# configuration steps in parallel, etc.
161+
set(
162+
subtargets
163+
compiler_rt-configure
164+
compiler_rt-build
165+
clib-configure
166+
clib-build
167+
cxxlibs-configure
168+
cxxlibs-build
169+
)
170+
set(subtarget_deps none ${subtargets})
171+
list(REMOVE_AT subtarget_deps 6)
172+
173+
foreach(subtarget subtarget_dep IN ZIP_LISTS subtargets subtarget_deps)
174+
add_custom_target(${subtarget}-all)
175+
if(NOT subtarget_dep STREQUAL "none")
176+
add_dependencies(${subtarget}-all ${subtarget_dep}-all)
177+
endif()
178+
endforeach()
179+
endif()
180+
134181
# Read the JSON file to load a multilib configuration.
135182
file(READ ${MULTILIB_JSON} multilib_json_str)
136183
string(JSON multilib_defs GET ${multilib_json_str} "libs")
@@ -218,11 +265,58 @@ foreach(lib_idx RANGE ${lib_count_dec})
218265
STEP_TARGETS build install
219266
USES_TERMINAL_CONFIGURE FALSE
220267
USES_TERMINAL_BUILD TRUE
221-
USES_TERMINAL_TEST TRUE
222268
LIST_SEPARATOR ,
223269
CONFIGURE_HANDLED_BY_BUILD TRUE
224270
TEST_EXCLUDE_FROM_MAIN TRUE
225271
)
272+
273+
if(ENABLE_PARALLEL_LIB_CONFIG OR ENABLE_PARALLEL_LIB_BUILD)
274+
# Create additional steps to configure/build the subprojects.
275+
# These are collected to be run together, so that all the
276+
# configuration steps can be run in parallel.
277+
# Each step should depend on the previous, with the first depending on the pre-defined
278+
# 'configure' step, and the pre-defined 'build' step depending on the last.
279+
set(subtarget_deps configure ${subtargets} build)
280+
list(SUBLIST subtarget_deps 0 6 subtarget_dependees)
281+
list(SUBLIST subtarget_deps 2 6 subtarget_dependers)
282+
283+
# First loop to add the steps and targets.
284+
foreach(subtarget subtarget_dependee IN ZIP_LISTS subtargets subtarget_dependees)
285+
# Enabling USES_TERMINAL puts the step in Ninja's "console" job pool, which
286+
# prevents the steps from being run in parallel since each must be given
287+
# exclusive access to the terminal. When disabled, the console won't be updated
288+
# with any output from the step until it completes.
289+
set(step_uses_terminal ON)
290+
set(step_extra_env "")
291+
if(${subtarget} MATCHES "-configure$" AND ENABLE_PARALLEL_LIB_CONFIG)
292+
set(step_uses_terminal OFF)
293+
elseif(${subtarget} MATCHES "-build$" AND ENABLE_PARALLEL_LIB_BUILD)
294+
set(step_uses_terminal OFF)
295+
set(step_extra_env ${CMAKE_COMMAND} -E env CMAKE_BUILD_PARALLEL_LEVEL=${PARALLEL_LIB_BUILD_LEVELS})
296+
endif()
297+
ExternalProject_Add_Step(
298+
runtimes-${variant}
299+
${subtarget}
300+
COMMAND ${step_extra_env} ${CMAKE_COMMAND} --build <BINARY_DIR> --target ${subtarget}
301+
DEPENDEES ${subtarget_dependee}
302+
DEPENDERS build
303+
USES_TERMINAL ${step_uses_terminal}
304+
)
305+
ExternalProject_Add_StepTargets(runtimes-${variant} ${subtarget})
306+
add_dependencies(${subtarget}-all runtimes-${variant}-${subtarget})
307+
endforeach()
308+
309+
# Second loop to set the steps that will depend on the new targets.
310+
foreach(subtarget subtarget_depender IN ZIP_LISTS subtargets subtarget_dependers)
311+
ExternalProject_Add_StepDependencies(
312+
runtimes-${variant}
313+
${subtarget_depender}
314+
${subtarget}-all
315+
)
316+
endforeach()
317+
endif()
318+
319+
# Add custom check targets.
226320
set(check_targets "")
227321
if(read_ENABLE_LIBC_TESTS)
228322
list(APPEND check_targets check-${C_LIBRARY})

arm-software/embedded/arm-runtimes/CMakeLists.txt

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,10 @@ set(compile_arch_flags "--target=${target_triple} ${COMPILE_FLAGS}")
211211
# flags, and requires a sysroot.
212212
set(lib_compile_flags "${compile_arch_flags} -ffunction-sections -fdata-sections -fno-ident --sysroot ${TEMP_LIB_DIR}")
213213

214-
# Declare this target now, since compiler-rt requires the dependency.
214+
# Generic target names for the C library.
215+
# Declare these now, since compiler-rt requires the 'install' dependency.
216+
add_custom_target(clib-configure)
217+
add_custom_target(clib-build)
215218
add_custom_target(clib-install)
216219

217220
###############################################################################
@@ -289,8 +292,8 @@ ExternalProject_Add(
289292
-DLLVM_CMAKE_DIR=${LLVM_BINARY_DIR}
290293
-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON
291294
${compiler_rt_test_cmake_args}
292-
STEP_TARGETS build install
293-
USES_TERMINAL_CONFIGURE FALSE
295+
STEP_TARGETS configure build install
296+
USES_TERMINAL_CONFIGURE TRUE
294297
USES_TERMINAL_BUILD TRUE
295298
USES_TERMINAL_INSTALL TRUE
296299
LIST_SEPARATOR ,
@@ -400,12 +403,13 @@ if(C_LIBRARY STREQUAL picolibc)
400403
<SOURCE_DIR>
401404
BUILD_COMMAND ${MESON_EXECUTABLE} compile
402405
INSTALL_COMMAND ${MESON_EXECUTABLE} install ${MESON_INSTALL_QUIET}
403-
USES_TERMINAL_CONFIGURE FALSE
406+
USES_TERMINAL_CONFIGURE TRUE
404407
USES_TERMINAL_BUILD TRUE
408+
USES_TERMINAL_INSTALL TRUE
405409
LIST_SEPARATOR ,
406410
CONFIGURE_HANDLED_BY_BUILD TRUE
407411
TEST_EXCLUDE_FROM_MAIN TRUE
408-
STEP_TARGETS build install
412+
STEP_TARGETS configure build install
409413
)
410414

411415
add_custom_target(check-picolibc)
@@ -524,12 +528,13 @@ if(C_LIBRARY STREQUAL newlib)
524528
<BINARY_DIR>/${target_triple}/libgloss/${cpu_family}/libcrt0-nosys.a
525529
${TEMP_LIB_DIR}/lib
526530
# FIXME: TEST_COMMAND?
527-
USES_TERMINAL_CONFIGURE FALSE
531+
USES_TERMINAL_CONFIGURE TRUE
528532
USES_TERMINAL_BUILD TRUE
533+
USES_TERMINAL_INSTALL TRUE
529534
# Always run the build command so that incremental builds are correct.
530535
CONFIGURE_HANDLED_BY_BUILD TRUE
531536
TEST_EXCLUDE_FROM_MAIN TRUE
532-
STEP_TARGETS install # FIXME: test?
537+
STEP_TARGETS configure build install # FIXME: test?
533538
)
534539
endif()
535540

@@ -621,11 +626,10 @@ if(C_LIBRARY STREQUAL llvmlibc)
621626
-DLLVM_ENABLE_RUNTIMES=libc
622627
-DLLVM_INCLUDE_TESTS=OFF # llvmlibc's tests require C++, so can't be built until llvmlibc can support libc++
623628
-DLLVM_LIBC_FULL_BUILD=ON
624-
STEP_TARGETS build install
625-
USES_TERMINAL_CONFIGURE FALSE
629+
STEP_TARGETS configure build install
630+
USES_TERMINAL_CONFIGURE TRUE
626631
USES_TERMINAL_BUILD TRUE
627632
USES_TERMINAL_INSTALL TRUE
628-
USES_TERMINAL_TEST TRUE
629633
LIST_SEPARATOR ,
630634
CONFIGURE_HANDLED_BY_BUILD TRUE
631635
INSTALL_COMMAND ${CMAKE_COMMAND} --install .
@@ -653,7 +657,7 @@ if(C_LIBRARY STREQUAL llvmlibc)
653657
${compiler_launcher_cmake_args}
654658
${common_llvmlibc_cmake_args}
655659
STEP_TARGETS build install
656-
USES_TERMINAL_CONFIGURE FALSE
660+
USES_TERMINAL_CONFIGURE TRUE
657661
USES_TERMINAL_BUILD TRUE
658662
USES_TERMINAL_INSTALL TRUE
659663
USES_TERMINAL_TEST TRUE
@@ -662,6 +666,8 @@ if(C_LIBRARY STREQUAL llvmlibc)
662666
)
663667
endif()
664668

669+
add_dependencies(clib-configure ${C_LIBRARY}-configure)
670+
add_dependencies(clib-build ${C_LIBRARY}-build)
665671
add_dependencies(clib-install ${C_LIBRARY}-install)
666672

667673
###############################################################################
@@ -762,11 +768,10 @@ if(ENABLE_CXX_LIBS)
762768
-DRUNTIME_VARIANT_NAME=${VARIANT}
763769
${cxxlibs_extra_cmake_options}
764770
${cxxlibs_test_cmake_options}
765-
STEP_TARGETS build install
766-
USES_TERMINAL_CONFIGURE FALSE
771+
STEP_TARGETS configure build install
772+
USES_TERMINAL_CONFIGURE TRUE
767773
USES_TERMINAL_BUILD TRUE
768774
USES_TERMINAL_INSTALL TRUE
769-
USES_TERMINAL_TEST TRUE
770775
LIST_SEPARATOR ,
771776
CONFIGURE_HANDLED_BY_BUILD TRUE
772777
)

0 commit comments

Comments
 (0)