diff mbox series

core: Use of percent_defconfig seems to impact performance

Message ID 20221019175151.1030079-1-nhed+buildroot@starry.com
State Superseded
Headers show
Series core: Use of percent_defconfig seems to impact performance | expand

Commit Message

Nevo Hed Oct. 19, 2022, 5:51 p.m. UTC
Noticed a significant slowdown with rise of number of external trees
in our env.  This slowdown seemed to be related to invocations if the
percent_defconfig function (GNU Make 4.3).

While I have not do a deep dive in analyzing the performance issue, it
felt like redefining the %_defconfig rule N times impact performance.

This patch makes %_defconfig a single rule which combines uses the
first return of a wildcard expression.

Timing (seconds) of `make pc_x86_64_bios_defconfig` with 1-8 external
trees:

    #Trees    Before    After
         1      0.38     0.37
         2      0.32     0.31
         3      0.31     0.33
         4      0.36     0.32
         5      0.45     0.35
         6      1.26     0.36
         7      9.10     0.36
         8     85.93     0.42

Signed-off-by: Nevo Hed <nhed+buildroot@starry.com>
---
 Makefile | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

Comments

Yann E. MORIN Oct. 19, 2022, 6:25 p.m. UTC | #1
Nevo, All,

On 2022-10-19 13:51 -0400, Nevo Hed spake thusly:
> Noticed a significant slowdown with rise of number of external trees
> in our env.  This slowdown seemed to be related to invocations if the
> percent_defconfig function (GNU Make 4.3).

We already have a pending patch to address this issue:
     https://patchwork.ozlabs.org/project/buildroot/patch/20220920194645.670432-1-yann.morin.1998@free.fr/

> While I have not do a deep dive in analyzing the performance issue, it
> felt like redefining the %_defconfig rule N times impact performance.

Please, no first-person sentences in commit log (i.e., no "I").

> This patch makes %_defconfig a single rule which combines uses the
> first return of a wildcard expression.

I can certainly see that this looks a bit simpler than my proposal.

Hoever, I am not too fond of it, because it defers the test that the
defconfig exists into the shell rather than the Makefile. I don't know,
maybe it is in fact better because we can print a more user-friendly
error message...

> Timing (seconds) of `make pc_x86_64_bios_defconfig` with 1-8 external
> trees:
> 
>     #Trees    Before    After
>          1      0.38     0.37
>          2      0.32     0.31
>          3      0.31     0.33
>          4      0.36     0.32
>          5      0.45     0.35
>          6      1.26     0.36
>          7      9.10     0.36
>          8     85.93     0.42

Weird. For me, starting at 6 external trees, the time to completion
directly exploded above the hour.

One interesting point, though is that the duration does not seem to be
much impacted by the number of bréexternal trees. Could you test with up
to 1000 trees, like I did?

Regards,
Yann E. MORIN.

> Signed-off-by: Nevo Hed <nhed+buildroot@starry.com>
> ---
>  Makefile | 13 ++++++-------
>  1 file changed, 6 insertions(+), 7 deletions(-)
> 
> diff --git a/Makefile b/Makefile
> index ec7c034ac1..a8298dd5fd 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1010,13 +1010,12 @@ oldconfig syncconfig olddefconfig: $(BUILD_DIR)/buildroot-config/conf outputmake
>  defconfig: $(BUILD_DIR)/buildroot-config/conf outputmakefile
>  	@$(COMMON_CONFIG_ENV) $< --defconfig$(if $(DEFCONFIG),=$(DEFCONFIG)) $(CONFIG_CONFIG_IN)
>  
> -define percent_defconfig
> -# Override the BR2_DEFCONFIG from COMMON_CONFIG_ENV with the new defconfig
> -%_defconfig: $(BUILD_DIR)/buildroot-config/conf $(1)/configs/%_defconfig outputmakefile
> -	@$$(COMMON_CONFIG_ENV) BR2_DEFCONFIG=$(1)/configs/$$@ \
> -		$$< --defconfig=$(1)/configs/$$@ $$(CONFIG_CONFIG_IN)
> -endef
> -$(eval $(foreach d,$(call reverse,$(TOPDIR) $(BR2_EXTERNAL_DIRS)),$(call percent_defconfig,$(d))$(sep)))
> +%_defconfig: $(BUILD_DIR)/buildroot-config/conf  outputmakefile
> +	@defconfig=$(firstword $(foreach d,\
> +		$(call reverse,$(TOPDIR) $(BR2_EXTERNAL_DIRS)),$(wildcard $(d)/configs/$@))); \
> +	if [ -z "$${defconfig}" ]; then echo "Can't find $@" >&2; exit 1; fi; \
> +	$(COMMON_CONFIG_ENV) BR2_DEFCONFIG=$${defconfig} \
> +		$< --defconfig=$${defconfig} $(CONFIG_CONFIG_IN)
>  
>  update-defconfig: savedefconfig
>  
> -- 
> 2.37.3
> 
> _______________________________________________
> buildroot mailing list
> buildroot@buildroot.org
> https://lists.buildroot.org/mailman/listinfo/buildroot
Nevo Hed Oct. 19, 2022, 10:11 p.m. UTC | #2
Yann, All

On Wed, Oct 19, 2022 at 2:25 PM Yann E. MORIN <yann.morin.1998@free.fr> wrote:
>
> Nevo, All,
>
> On 2022-10-19 13:51 -0400, Nevo Hed spake thusly:
> > Noticed a significant slowdown with rise of number of external trees
> > in our env.  This slowdown seemed to be related to invocations if the
> > percent_defconfig function (GNU Make 4.3).
>
> We already have a pending patch to address this issue:
>      https://patchwork.ozlabs.org/project/buildroot/patch/20220920194645.670432-1-yann.morin.1998@free.fr/

Sorry, I forgot to look in patchwork and just saw that it is still not
addressed in master (my actual project uses the 2022.02 tag)

> > This patch makes %_defconfig a single rule which combines uses the
> > first return of a wildcard expression.
>
> I can certainly see that this looks a bit simpler than my proposal.
>
> Hoever, I am not too fond of it, because it defers the test that the
> defconfig exists into the shell rather than the Makefile. I don't know,
> maybe it is in fact better because we can print a more user-friendly
> error message...

Agreed on that front - I was even toying using `vpath` but it didn't pan out
(probably because I have not used that feature in a long time).

> > Timing (seconds) of `make pc_x86_64_bios_defconfig` with 1-8 external
> > trees:
> >
> >     #Trees    Before    After
> >          1      0.38     0.37
> >          2      0.32     0.31
> >          3      0.31     0.33
> >          4      0.36     0.32
> >          5      0.45     0.35
> >          6      1.26     0.36
> >          7      9.10     0.36
> >          8     85.93     0.42
>
> Weird. For me, starting at 6 external trees, the time to completion
> directly exploded above the hour.
>
> One interesting point, though is that the duration does not seem to be
> much impacted by the number of bréexternal trees. Could you test with up
> to 1000 trees, like I did?

For 1000, I got 15.68 - so the same neighborhood as yours.
Maybe deltas in the 6-7 trees might be hardware related (cache sizes,
FS types etc - strace-ing the make in the many-trees case is a wild
spam storm)

Addendum:
I originally prepared a cover letter but am not a frequent user of
git-send-email and it didn't send.  I'll paste it below - please note
the option of some implicit rule resets.


===========================================================

From 6a2fa74edf5bd3b254fc7c49ebe465280e8e8902 Mon Sep 17 00:00:00 2001
From: Nevo Hed <nhed+buildroot@starry.com>
Date: Wed, 19 Oct 2022 13:32:14 -0400
Subject: [PATCH 0/1] core: Use of percent_defconfig seems to impact performance


Noticing serious perforate issues on `make X_defconfig` with growth of
number of external trees.  At 7 trees it is somewhat tolerable but at 8
trees we have seen it take 80-110 seconds.

Numbers for before and after the change in commit itself.

I do not know for sure what the underlying issue is but looking at some
`strace` and `make -d` output it feels like an implosion of implicit rules
being evaluated.

Adding the resets as suggested here
https://lists.gnu.org/archive/html/help-make/2002-11/msg00062.html seems to
help a lot but not as much as the attached patch.

--8<------------
%: %,v
%: RCS/%,v
%: RCS/%
%: s.%
%: SCCS/s.%
--8<------------

Inlined below a script to repro this issue, not sure if this functionality
should be integrated anywhere or just left in the mailing list.  By
default the script runs the scenario from 1 tree to 8 trees.  Running with
param (1-10) will allow a single run with that many trees.


--8<------------
#!/usr/bin/env bash

declare treedirs=()

declare -r cfg="pc_x86_64_bios_defconfig"

function setup_dir {
    for tree in tree{1..10}; do
        local treedir="defconf_test/${tree}"

        mkdir -p "${treedir}/configs"
        touch "${treedir}/Config.in"
        touch "${treedir}/external.mk"
        echo -e "name: ${tree^^}\ndesc: ${tree^}" > "${treedir}/external.desc"

        cp "./configs/${cfg}" "${treedir}/configs/${tree}_${cfg}"

        treedirs+=( "${treedir}" )
    done
}

function run_one {
    local count="${1}"
    local br2_external=$(IFS=':'; echo "${treedirs[*]:0:count}")
    echo "Defconfig with ${count} trees (BR2_EXTERNAL=\"${br2_external}\")"
    /usr/bin/time -f "${count},%e" /bin/make
BR2_EXTERNAL="${br2_external}" "${cfg}"
}

# Main
setup_dir
if [ -n "${1}" ]; then
    run_one "${1}"
else
    for i in {1..8}; do
        run_one $i
    done
fi
--8<------------
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index ec7c034ac1..a8298dd5fd 100644
--- a/Makefile
+++ b/Makefile
@@ -1010,13 +1010,12 @@  oldconfig syncconfig olddefconfig: $(BUILD_DIR)/buildroot-config/conf outputmake
 defconfig: $(BUILD_DIR)/buildroot-config/conf outputmakefile
 	@$(COMMON_CONFIG_ENV) $< --defconfig$(if $(DEFCONFIG),=$(DEFCONFIG)) $(CONFIG_CONFIG_IN)
 
-define percent_defconfig
-# Override the BR2_DEFCONFIG from COMMON_CONFIG_ENV with the new defconfig
-%_defconfig: $(BUILD_DIR)/buildroot-config/conf $(1)/configs/%_defconfig outputmakefile
-	@$$(COMMON_CONFIG_ENV) BR2_DEFCONFIG=$(1)/configs/$$@ \
-		$$< --defconfig=$(1)/configs/$$@ $$(CONFIG_CONFIG_IN)
-endef
-$(eval $(foreach d,$(call reverse,$(TOPDIR) $(BR2_EXTERNAL_DIRS)),$(call percent_defconfig,$(d))$(sep)))
+%_defconfig: $(BUILD_DIR)/buildroot-config/conf  outputmakefile
+	@defconfig=$(firstword $(foreach d,\
+		$(call reverse,$(TOPDIR) $(BR2_EXTERNAL_DIRS)),$(wildcard $(d)/configs/$@))); \
+	if [ -z "$${defconfig}" ]; then echo "Can't find $@" >&2; exit 1; fi; \
+	$(COMMON_CONFIG_ENV) BR2_DEFCONFIG=$${defconfig} \
+		$< --defconfig=$${defconfig} $(CONFIG_CONFIG_IN)
 
 update-defconfig: savedefconfig