[PATCH 0/8] Makefile: optimize noop runs, add shared.mak

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

 



This topic is split-up from v4 of ab/make-dependency[1], and is the
last topic I've split up from that which I'll submit before the
outstanding ones land.

This speeds up noop runs of "make" by a lot. After a "make" running a
"make -j1" with this is ~1.5 faster than on "master"[2], and around 3x
as fast with "make -j1 NO_TCLTK=Y" (the TCL part takes a lot of time,
but that's another matter).

As shown in 8/8 the only meaningful change to existing Makefile rules
makes things simpler, while making them much faster. In 3/8 a rather
trivial change to use ":=" instead of "=" for a "$(shell)" assignment
likewise speeds things up.

The "shared.mak" introduced while getting to 8/8 will then be used by
follow-on topics to make introducing shared behavior (particularly via
$(call), which we can now rely on) easier.

The range-diff to [1] shows changes from that topic. Most of them are
from pulling this topic out of those larger changes. I also re-ran all
the benchmarks & updated those in the commit messages.

1. https://lore.kernel.org/git/cover-v4-00.23-00000000000-20211117T101807Z-avarab@xxxxxxxxx/
2. $ git -c hyperfine.hook.setup= hyperfine -L rev origin/master,HEAD~0 -s 'make' 'make -j1' --warmup 1
   Benchmark 1: make -j1' in 'origin/master
     Time (mean ± σ):     563.1 ms ±  14.5 ms    [User: 453.3 ms, System: 117.6 ms]
     Range (min … max):   542.7 ms … 595.0 ms    10 runs

   Benchmark 2: make -j1' in 'HEAD~0
     Time (mean ± σ):     362.5 ms ±   4.5 ms    [User: 299.7 ms, System: 73.9 ms]
     Range (min … max):   356.9 ms … 369.4 ms    10 runs

   Summary
     'make -j1' in 'HEAD~0' ran
       1.55 ± 0.04 times faster than 'make -j1' in 'origin/master'
3. git -c hyperfine.hook.setup= hyperfine -L rev origin/master,HEAD~0 -s 'make' 'make -j1 NO_TCLTK=Y' --warmup 1
   Benchmark 1: make -j1 NO_TCLTK=Y' in 'origin/master
     Time (mean ± σ):     291.1 ms ±  24.1 ms    [User: 225.6 ms, System: 68.9 ms]
     Range (min … max):   275.0 ms … 353.9 ms    10 runs

   Benchmark 2: make -j1 NO_TCLTK=Y' in 'HEAD~0
     Time (mean ± σ):      95.8 ms ±   0.7 ms    [User: 81.9 ms, System: 18.1 ms]
     Range (min … max):    94.8 ms …  98.4 ms    31 runs

   Summary
     'make -j1 NO_TCLTK=Y' in 'HEAD~0' ran
       3.04 ± 0.25 times faster than 'make -j1 NO_TCLTK=Y' in 'origin/master'

Ævar Arnfjörð Bjarmason (8):
  Makefiles: add "shared.mak", move ".DELETE_ON_ERROR" to it
  Makefile: disable GNU make built-in wildcard rules
  Makefile: define $(LIB_H) in terms of $(FIND_SOURCE_FILES)
  Makefile: move ".SUFFIXES" rule to shared.mak
  Makefile: move $(comma), $(empty) and $(space) to shared.mak
  Makefile: add "$(QUIET)" boilerplate to shared.mak
  Makefile: use $(wspfx) for $(QUIET...) in shared.mak
  Makefiles: add and use wildcard "mkdir -p" template

 Documentation/Makefile |  63 +++-------------------
 Makefile               | 118 +++++++++++++----------------------------
 config.mak.uname       |   1 -
 shared.mak             | 109 +++++++++++++++++++++++++++++++++++++
 t/Makefile             |   3 ++
 t/interop/Makefile     |   3 ++
 templates/Makefile     |   8 ++-
 7 files changed, 160 insertions(+), 145 deletions(-)
 create mode 100644 shared.mak

Range-diff:
 1:  1621ca72c1d <  -:  ----------- Makefile: don't invoke msgfmt with --statistics
 2:  b7c36c9fea0 <  -:  ----------- Makefile: don't set up "perl/build" rules under NO_PERL=Y
 3:  29b000eb0f1 <  -:  ----------- Makefile: use "=" not ":=" for po/* and perl/*
 4:  daead5ec293 <  -:  ----------- Makefile: clean perl/build/ even with NO_PERL=Y
 5:  3c987590740 <  -:  ----------- Makefile: remove "mv $@ $@+" dance redundant to .DELETE_ON_ERROR
 6:  b57f582ccd3 <  -:  ----------- Makefile: guard Perl-only variable assignments
 7:  fcdee92f64c <  -:  ----------- Makefile: change "ifndef NO_PERL" to "ifdef NO_PERL"
 8:  1e25b532ca2 <  -:  ----------- Makefile: adjust Perl-related comments & whitespace
 9:  77d9855bfcf <  -:  ----------- Makefile: correct "GIT-PERL-{DEFINES,HEADER}" dependency graph
10:  6004cdcd8d9 <  -:  ----------- Makefile: create a GIT-PYTHON-DEFINES, like "PERL"
11:  17b30e96057 <  -:  ----------- Makefile: stop needing @@GIT_VERSION@@ in *.perl scripts
12:  30ddf7da2c8 !  1:  f74b47662b7 Makefiles: add "shared.mak", move ".DELETE_ON_ERROR" to it
    @@ shared.mak (new)
     +#
     +#    info make --index-search=.DELETE_ON_ERROR
     +.DELETE_ON_ERROR:
    +
    + ## t/Makefile ##
    +@@
    ++# Import tree-wide shared Makefile behavior and libraries
    ++include ../shared.mak
    ++
    + # Run tests
    + #
    + # Copyright (c) 2005 Junio C Hamano
    +
    + ## t/interop/Makefile ##
    +@@
    ++# Import tree-wide shared Makefile behavior and libraries
    ++include ../../shared.mak
    ++
    + -include ../../config.mak
    + export GIT_TEST_OPTIONS
    + 
    +
    + ## templates/Makefile ##
    +@@
    ++# Import tree-wide shared Makefile behavior and libraries
    ++include ../shared.mak
    ++
    + # make and install sample templates
    + 
    + ifndef V
21:  dd569a59c74 !  2:  b0c63abe091 Makefile: disable GNU make built-in wildcard rules
    @@ Commit message
         Makefile: disable GNU make built-in wildcard rules
     
         Override built-in rules of GNU make that use a wildcard target. This
    -    speeds things up significantly as we don't need to stat() so many
    -    files just in case we'd be able to retrieve their contents from RCS or
    -    SCCS. See [1] for an old mailing list discussion about how to disable
    -    these.
    -
    -    This gives us a noticeable speedup on a no-op run:
    -
    -        $ git hyperfine -L rev HEAD~1,HEAD~0 -s 'make -j8 all NO_TCLTK=Y' 'make NO_TCLTK=Y' --warmup 10 -M 10
    -        Benchmark 1: make NO_TCLTK=Y' in 'HEAD~1
    -          Time (mean ± σ):     182.2 ms ±   4.1 ms    [User: 146.8 ms, System: 49.5 ms]
    -          Range (min … max):   179.6 ms … 193.4 ms    10 runs
    -
    -        Benchmark 2: make NO_TCLTK=Y' in 'HEAD~0
    -          Time (mean ± σ):     167.9 ms ±   1.3 ms    [User: 127.8 ms, System: 55.6 ms]
    -          Range (min … max):   166.1 ms … 170.4 ms    10 runs
    -
    -        Summary
    -          'make NO_TCLTK=Y' in 'HEAD~0' ran
    -            1.09 ± 0.03 times faster than 'make NO_TCLTK=Y' in 'HEAD~1'
    -
    -    Running the same except with 'strace -c -S calls make' as the
    -    benchmark command shows (under --show-output) that we went from ~7716
    -    syscalls to ~7519, mostly a reduction in [l]stat().
    -
    -    We could also invoke make with "-r" by setting "MAKEFLAGS = -r" early,
    -    adding a "-r" variant to the above benchmark shows that it may be 1.01
    -    or so faster (but in my tests, always with a much bigger error
    -    bar). But doing so is a much bigger hammer, since it will disable all
    -    built-in rules, some (all?) of which can be seen with:
    +    can speeds things up significantly as we don't need to stat() so many
    +    files. GNU make does that by default to see if it can retrieve their
    +    contents from RCS or SCCS. See [1] for an old mailing list discussion
    +    about how to disable these.
    +
    +    The speed-up may wary. I've seen 1-10% depending on the speed of the
    +    local disk, caches, -jN etc. Running:
    +
    +        strace -f -c -S calls make -j1 NO_TCLTK=Y
    +
    +    Shows that we reduce the number of syscalls we make, mostly in "stat"
    +    calls.
    +
    +    We could also invoke make with "-r" by setting "MAKEFLAGS = -r"
    +    early. Doing so might make us a bit faster still. But doing so is a
    +    much bigger hammer, since it will disable all built-in rules,
    +    some (all?) of which can be seen with:
     
             make -f/dev/null -p | grep -v -e ^# -e ^$
     
22:  4168a7e3b30 !  3:  c6c6f7cf8d8 Makefile: define $(LIB_H) in terms of $(FIND_SOURCE_FILES)
    @@ Commit message
     
         This speeds things up a lot:
     
    -        $ git -c hyperfine.hook.setup= hyperfine -L rev HEAD~1,HEAD~0 -s 'make -j8 all NO_TCLTK=Y' 'make NO_TCLTK=Y' --war
    -        mup 10 -M 10
    -        Benchmark 1: make NO_TCLTK=Y' in 'HEAD~1
    -          Time (mean ± σ):      99.5 ms ±   0.5 ms    [User: 83.4 ms, System: 20.7 ms]
    -          Range (min … max):    98.8 ms … 100.2 ms    10 runs
    +        $ git -c hyperfine.hook.setup= hyperfine -L rev HEAD~1,HEAD~0 -s 'make NO_TCLTK=Y' 'make -j1 NO_TCLTK=Y' --warmup 10 -M 10
    +        Benchmark 1: make -j1 NO_TCLTK=Y' in 'HEAD~1
    +          Time (mean ± σ):     159.9 ms ±   6.8 ms    [User: 137.2 ms, System: 28.0 ms]
    +          Range (min … max):   154.6 ms … 175.9 ms    10 runs
     
    -        Benchmark 2: make NO_TCLTK=Y' in 'HEAD~0
    -          Time (mean ± σ):      69.4 ms ±   0.5 ms    [User: 58.8 ms, System: 14.2 ms]
    -          Range (min … max):    68.9 ms …  70.3 ms    10 runs
    +        Benchmark 2: make -j1 NO_TCLTK=Y' in 'HEAD~0
    +          Time (mean ± σ):     100.0 ms ±   1.3 ms    [User: 84.2 ms, System: 20.2 ms]
    +          Range (min … max):    98.8 ms … 102.8 ms    10 runs
     
             Summary
    -          'make NO_TCLTK=Y' in 'HEAD~0' ran
    -            1.43 ± 0.01 times faster than 'make NO_TCLTK=Y' in 'HEAD~1'
    +          'make -j1 NO_TCLTK=Y' in 'HEAD~0' ran
    +            1.60 ± 0.07 times faster than 'make -j1 NO_TCLTK=Y' in 'HEAD~1'
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@xxxxxxxxx>
     
    @@ Makefile: GENERATED_H += hook-list.h
      LIB_OBJS += abspath.o
      LIB_OBJS += add-interactive.o
     @@ Makefile: perl/build/man/man3/Git.3pm: perl/Git.pm
    - 	$(call mkdir_p_parent_template)
    - 	$(QUIET_GEN)pod2man $< $@
    + 	$(QUIET_GEN)mkdir -p $(dir $@) && \
    + 	pod2man $< $@
      
     -FIND_SOURCE_FILES = ( \
     -	git ls-files \
23:  48a3927d972 !  4:  ed64cd1bd4a Makefile: move ".SUFFIXES" rule to shared.mak
    @@ Commit message
     
         This doesn't benefit the main Makefile at all, since it already had
         the rule, but since we're including shared.mak in other Makefiles
    -    starts to benefit them. E.g. running the 'man" target is now ~1.3x
    -    faster:
    +    starts to benefit them. E.g. running the 'man" target is now faster:
     
    -        $ git -c hyperfine.hook.setup= hyperfine -L rev HEAD~1,HEAD~0 -s 'make -j8 -C Documentation man' 'make -C Documentation man' --warmup 10 -M 10
    -        Benchmark 1: make -C Documentation man' in 'HEAD~1
    -          Time (mean ± σ):      87.2 ms ±   1.0 ms    [User: 79.3 ms, System: 10.8 ms]
    -          Range (min … max):    86.3 ms …  89.7 ms    10 runs
    +        $ git -c hyperfine.hook.setup= hyperfine -L rev HEAD~1,HEAD~0 -s 'make -C Documentation man' 'make -C Documentation -j1 man'
    +        Benchmark 1: make -C Documentation -j1 man' in 'HEAD~1
    +          Time (mean ± σ):     121.7 ms ±   8.8 ms    [User: 105.8 ms, System: 18.6 ms]
    +          Range (min … max):   112.8 ms … 148.4 ms    26 runs
     
    -        Benchmark 2: make -C Documentation man' in 'HEAD~0
    -          Time (mean ± σ):      64.5 ms ±   0.6 ms    [User: 54.5 ms, System: 13.0 ms]
    -          Range (min … max):    63.8 ms …  65.7 ms    10 runs
    +        Benchmark 2: make -C Documentation -j1 man' in 'HEAD~0
    +          Time (mean ± σ):      97.5 ms ±   8.0 ms    [User: 80.1 ms, System: 20.1 ms]
    +          Range (min … max):    89.8 ms … 111.8 ms    32 runs
     
             Summary
    -          'make -C Documentation man' in 'HEAD~0' ran
    -            1.35 ± 0.02 times faster than 'make -C Documentation man' in 'HEAD~1'
    +          'make -C Documentation -j1 man' in 'HEAD~0' ran
    +            1.25 ± 0.14 times faster than 'make -C Documentation -j1 man' in 'HEAD~1'
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@xxxxxxxxx>
     
13:  f378a7dc35e =  5:  1749085b929 Makefile: move $(comma), $(empty) and $(space) to shared.mak
14:  13cbb851d32 <  -:  ----------- Makefile: re-add and use the "shellquote" macros
15:  337953e4994 <  -:  ----------- Makefile: add a "TRACK_template" for GIT-*{FLAGS,DEFINES,...}
16:  5bb597c1993 !  6:  c25284b24cf Makefile: add "$(QUIET)" boilerplate to shared.mak
    @@ config.mak.uname: vcxproj:
      
     
      ## shared.mak ##
    -@@ shared.mak: space = $(empty) $(empty)
    - wspfx = $(space)$(space)$(space)
    - wspfx_sq = $(call shellquote,$(wspfx))
    - 
    +@@
    + comma = ,
    + empty =
    + space = $(empty) $(empty)
    ++
     +### Quieting
     +## common
     +QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
    @@ shared.mak: space = $(empty) $(empty)
     +	export V
     +endif
     +endif
    -+
    - ### Templates
    - 
    - ## Template for making a GIT-SOMETHING, which changes if a
     
      ## templates/Makefile ##
     @@
17:  3c4d0589667 <  -:  ----------- Makefile: use $(wspfx) for $(QUIET...) in shared.mak
 -:  ----------- >  7:  3daef7672be Makefile: use $(wspfx) for $(QUIET...) in shared.mak
18:  be5882b2c99 !  8:  aca560ca410 Makefiles: add and use wildcard "mkdir -p" template
    @@ Commit message
         shows that this is faster, in addition to being less verbose and more
         reliable (this uses my "git-hyperfine" thin wrapper for "hyperfine"[3]):
     
    -        $ git hyperfine -L rev HEAD~0,HEAD~1 -b 'make -C Documentation lint-docs' -p 'rm -rf Documentation/.build' 'make -C Documentation lint-docs'
    -        Benchmark 1: make -C Documentation lint-docs' in 'HEAD~0
    -          Time (mean ± σ):      2.129 s ±  0.011 s    [User: 1.840 s, System: 0.321 s]
    -          Range (min … max):    2.121 s …  2.158 s    10 runs
    +        $ git -c hyperfine.hook.setup= hyperfine -L rev HEAD~1,HEAD~0 -s 'make -C Documentation lint-docs' -p 'rm -rf Documentation/.build' 'make -C Documentation -j1 lint-docs'
    +        Benchmark 1: make -C Documentation -j1 lint-docs' in 'HEAD~1
    +          Time (mean ± σ):      2.914 s ±  0.062 s    [User: 2.449 s, System: 0.489 s]
    +          Range (min … max):    2.834 s …  3.020 s    10 runs
     
    -        Benchmark 2: make -C Documentation lint-docs' in 'HEAD~1
    -          Time (mean ± σ):      2.659 s ±  0.002 s    [User: 2.306 s, System: 0.397 s]
    -          Range (min … max):    2.657 s …  2.662 s    10 runs
    +        Benchmark 2: make -C Documentation -j1 lint-docs' in 'HEAD~0
    +          Time (mean ± σ):      2.315 s ±  0.062 s    [User: 1.950 s, System: 0.386 s]
    +          Range (min … max):    2.229 s …  2.397 s    10 runs
     
             Summary
    -          'make -C Documentation lint-docs' in 'HEAD~0' ran
    -            1.25 ± 0.01 times faster than 'make -C Documentation lint-docs' in 'HEAD~1'
    +          'make -C Documentation -j1 lint-docs' in 'HEAD~0' ran
    +            1.26 ± 0.04 times faster than 'make -C Documentation -j1 lint-docs' in 'HEAD~1'
     
         So let's use that pattern both for the "lint-docs" target, and a few
         miscellaneous other targets.
    @@ Makefile: all:: $(MOFILES)
     +	$(call mkdir_p_parent_template)
     +	$(QUIET_MSGFMT)$(MSGFMT) -o $@ $<
      
    - ifndef NO_PERL
    - LIB_PERL = $(wildcard perl/Git.pm perl/Git/*.pm perl/Git/*/*.pm perl/Git/*/*/*.pm)
    -@@ Makefile: LIB_CPAN = $(wildcard perl/FromCPAN/*.pm perl/FromCPAN/*/*.pm)
    - LIB_CPAN_GEN = $(patsubst perl/%.pm,perl/build/lib/%.pm,$(LIB_CPAN))
    + LIB_PERL := $(wildcard perl/Git.pm perl/Git/*.pm perl/Git/*/*.pm perl/Git/*/*/*.pm)
    + LIB_PERL_GEN := $(patsubst perl/%.pm,perl/build/lib/%.pm,$(LIB_PERL))
    +@@ Makefile: NO_PERL_CPAN_FALLBACKS_SQ = $(subst ','\'',$(NO_PERL_CPAN_FALLBACKS))
    + endif
      
      perl/build/lib/%.pm: perl/%.pm GIT-PERL-DEFINES
     -	$(QUIET_GEN)mkdir -p $(dir $@) && \
    @@ Makefile: LIB_CPAN = $(wildcard perl/FromCPAN/*.pm perl/FromCPAN/*/*.pm)
     +	$(QUIET_GEN) \
      	sed -e 's|@@LOCALEDIR@@|$(perl_localedir_SQ)|g' \
      	    -e 's|@@NO_GETTEXT@@|$(NO_GETTEXT_SQ)|g' \
    - 	    -e 's|@@NO_PERL_CPAN_FALLBACKS@@|$(call shq,$(NO_PERL_CPAN_FALLBACKS))|g' \
    -@@ Makefile: endif
    + 	    -e 's|@@NO_PERL_CPAN_FALLBACKS@@|$(NO_PERL_CPAN_FALLBACKS_SQ)|g' \
    + 	< $< > $@
      
    - # install-man depends on Git.3pm even with NO_PERL=Y
      perl/build/man/man3/Git.3pm: perl/Git.pm
     -	$(QUIET_GEN)mkdir -p $(dir $@) && \
     -	pod2man $< $@
     +	$(call mkdir_p_parent_template)
     +	$(QUIET_GEN)pod2man $< $@
      
    - FIND_SOURCE_FILES = ( \
    - 	git ls-files \
    + $(ETAGS_TARGET): $(FOUND_SOURCE_FILES)
    + 	$(QUIET_GEN)$(RM) $@+ && \
     @@ Makefile: test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(
      all:: $(TEST_PROGRAMS) $(test_bindir_programs)
      
    @@ Makefile: test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_
      ## shared.mak ##
     @@ shared.mak: ifndef V
      	QUIET          = @
    - 	QUIET_GEN      = @echo $(wspfx_sq) GEN $@;
    + 	QUIET_GEN      = @echo $(wspfx_SQ) GEN $@;
      
    -+	QUIET_MKDIR_P_PARENT  = @echo $(wspfx_sq) MKDIR -p $(@D);
    ++	QUIET_MKDIR_P_PARENT  = @echo $(wspfx_SQ) MKDIR -p $(@D);
     +
      ## Used in "Makefile"
    - 	QUIET_CC       = @echo $(wspfx_sq) CC $@;
    - 	QUIET_AR       = @echo $(wspfx_sq) AR $@;
    + 	QUIET_CC       = @echo $(wspfx_SQ) CC $@;
    + 	QUIET_AR       = @echo $(wspfx_SQ) AR $@;
     @@ shared.mak: ifndef V
    + 	export V
      endif
      endif
    - 
    -+## Helpers
    ++
    ++### Templates
    ++
    ++## mkdir_p_parent: lazily "mkdir -p" the path needed for a $@
    ++## file. Uses $(wildcard) to avoid the "mkdir -p" if it's not
    ++## needed.
    ++##
    ++## Is racy, but in a good way; we might redundantly (and safely)
    ++## "mkdir -p" when running in parallel, but won't need to exhaustively
    ++## individual rules for "a" -> "prefix" -> "dir" -> "file" if given a
    ++## "a/prefix/dir/file". This can instead be inserted at the start of
    ++## the "a/prefix/dir/file" rule.
     +define mkdir_p_parent_template
     +$(if $(wildcard $(@D)),,$(QUIET_MKDIR_P_PARENT)$(shell mkdir -p $(@D)))
     +endef
    -+
    - ### Templates
    - 
    - ## Template for making a GIT-SOMETHING, which changes if a
19:  2710f8af6cd <  -:  ----------- Makefile: correct the dependency graph of hook-list.h
20:  59f22a0269a <  -:  ----------- Makefile: use $(file) I/O instead of "FORCE" when possible
-- 
2.34.1.1119.g7a3fc8778ee




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux