help-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Performance loss when trying to use grouped targets


From: Stephen Touset
Subject: Performance loss when trying to use grouped targets
Date: Mon, 4 Mar 2024 20:16:20 +0100

I have a large project whose build process consists of running some
independent sub-build steps within several hundred directories. As a
preparatory step, we copy each of these directories into a temporary build
location:

# collect a list of all source files
sources := $(shell find src -mindepth 2 -type f)

define build_component
# drop a flag that indicates a successful build
build/.$(1): build/$(1)
[actual build process goes here]
touch $$@

# the build directory depends upon each of the individual files within it
build/$(1): $$(patsubst src/%,build/%,$(filter src/$(1)/%,$(sources))

# every file in each build subdirectory depends on its corresponding file
# in the src directory, but they can all be copied into place in one step
$$(patsubst src/%,build/%,$(filter src/$(1)/%,$(sources))) &: $$(filter
src/$(1)/%,$(sources))
rm -rf build/$(1)
cp -a src/$(1) build/$(1)

.SECONDARY: build/.$(1) build/$(1)
.SECONDARY: $$(patsubst src/%,build/%,$(filter src/$(1)/%,$(sources))
ended

# generate the build steps for each component
$(foreach component,$(components),$(eval $(call
build_component,$(component))))

We jump through all these hoops for some important reasons. The build
process often moves and/or removes files inside of the directories it’s
working with. The above rules ensures that if the build is interrupted or
fails a some point, if any files are missing for some component of the
build, we’ll blow away that directory and start from scratch. It also
ensures that each component is rebuilt if any of its individual sources
changes.

This is working well, but it doesn’t perform particularly well thanks to
hundreds of independent calls to cp that could theoretically be merged into
one. This is where I’m running into problems. I rewrote the patsubst rule
and its corresponding entry in .SECONDARY:

# every file in the build directory depends on its corresponding file in
# thee src directory, but they can all be copied into place in one step
$$(patsubst src/%,build/%,$(sources)) &: $(sources)
rm -rf build
cp -a src build

.SECONDARY: $$(patsubst src/%,build/%,$(sources))

When run in one thread, this works as expected. It cuts down significantly
on the overall build speed. When run in parallel, though, it unfortunately
causes a massive slowdown. It seems that make isn’t efficiently sharing
analysis info amongst parallel workers. It does function as expected, but
debug output shows that tens of millions of Pruning file steps are
generated before any work is performed.

I think the fundamental issue is that this rule doesn’t quite express what
I want. The above rule says, roughly, that “each file in the build
directory depends upon *all* the files in the source directory”, and so a
freshness check for each individual file in the build fans out to a
freshness check on every file in the source. This needs to be done over and
over and over. Is it possible to have our cake and eat it too? Is there a
way to write a rule that expresses something closer to “each file in the
build directory depends upon its corresponding file in the source
directory” but still uses grouped targets?

Note: The above rules were simplified for clarity from what’s in our actual
build system. I may have typo’d something in the process. I apologize
preemptively if there winds up being any confusion caused by this.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]