=head1 NAME
makepp_speedup -- How to make makepp faster
=
for
vc
$Id
: makepp_speedup.pod,v 1.10 2008/05/21 21:58:58 pfeiffer Exp $
=head1 DESCRIPTION
So you think makepp is slow? Did you check 1.50 which is noticeably faster?
But granted, it's still slow, especially
if
you come from GNU make. This is
because it conscientiously checks all those things where gmake gives you a
headache, ignoring lots of dependencies (the "I think I need to gmake clean to
get rid of a mysterious bug" syndrome). If you suspect some Perl code you
added to your makefiles might be at fault, take a look at L<perl_performance>.
But there are a few things you can
do
to squeeze out more speed. Some of the
things are labelled unsafe, in the sence that you're asking makepp not to
check or
do
certain things, which you think are not needed. If these things
would have been necessary, the build may not be correct. Luckily this problem
will be temporary, however. It will get corrected as soon as you let makepp
do
all checks.
You can combine several of these tips to increase the
time
gain even more.
=head2 Safe Methods
=head3 Use a Faster Perl
Within version 5.8, all are roughly the same, only 5.8.7 is a bit faster.
Within version 5.6,
each
newer subversion is noticeably faster. And, at least
for
makepp, 5.6.2 is 10% faster than 5.8.7. If you don't need any newer Perl
features in your Makeppfiles, I suggest getting a B<5.6.2
with
Digest::MD5>
added.
Tuning your Perl can also help, like not compiling it
for
64 bits, which
makepp doesn
't need. For example ActiveState'
s build
the Perl 5.8.7 that comes
with
SuSE Linux 10.0.
=head3 Include as Little as Possible
Each additional file you include is doubly penalizing. On the one hand, the
compiler must look
for
and at all those files. You don't notice this so much,
because it's just a little extra per compiler call. On the other hand makepp
must look too, to find dependencies and figure out whether they incur a
rebuild. Then it can seem to stall,
while
it is digesting a lot of
dependencies at once.
An absolutely deadly variant is the project master include file, which in turn
conveniently includes anything you might need. The result is that any header
file change leads to a full build. Even without a change, makepp must think
about all those headers again,
for
every source you compile. Just a tiny
effort, since this is cached, but thousands of files can make this staggering.
It may be cumbersome to figure out the minimal set of includes, and to cleanup
those
no
longer needed, but it really pays off. If anybody knows a tool that
can identify which files get included unnecessarily, I'd be glad to mention it
here!
=head3 Build as Little as You Need
If you have a
default
target which makes several programs, then makepp will
have to check all their dependencies, right down to the smallest header file.
But maybe you want to test your change
with
only one of those programs.
Then you would call makepp
with
an explicit target. The less modules or
headers all those programs have in common, the greater the benefit of not
letting makepp check them all.
Say your top level Makeppfile
has
this rule:
$(phony all): proggie1 proggie2 $(only_phony_targets */**/all)
Then you would call things like
$ makepp proggie2
$ makepp proggie1 dir/subdir/proggie27
=head3 Use preferred makefile names
Makepp looks
for
makefiles (
unless
you specify them explicitly on the command
line or
with
C<load-makefile>) in the order F<RootMakeppfile>,
F<RootMakeppfile.mk>, F<Makeppfile> and F<Makeppfile.mk>, followed by the
classical makefile names. (The F<.mk> variants are
for
purely suffix-based
systems.)
So,
if
you
use
F<RootMakeppfile> at the root of your build tree, and
F<Makeppfile> everywhere
else
, the files will be found slightly faster.
Makepp will also have a slightly smaller memory consumption (caching the fact
that the other names don't exist), which also means speed through less memory
management.
Likewise
if
you have a statement
include standard
there will first be an attempt to find F<standard.makepp>, so you might as well
=head3 Have as few rules as you need
Makepp keeps track not only of existant files, but also of any it learns to
create. (That's why it offers reliable wildcards like F<*.o>.) The price
for
this power is a lot of management. So,
if
you
tell
it how to create a F<.o>
from a F<.c>, that's fine, because it will happen
for
most
if
not all
candidates.
But
if
you
tell
it how to
link
any suffixless executable from a like named
F<.o>, that's expensive, because it will probably only happen
for
a small part
of them (those that contain a main function), but the basis will get laid
for
all. You have to weigh the comfort of a linker pattern rule, against the
efficiency of individual linker rules.
If you don't
use
any of them, you should also turn off the builtin rules
with
:
makepp_no_builtin = 1
If you
do
use
them, but,
for
the reasons explained above, not the builtin
linker rules, you should turn those off
with
:
makepp_no_builtin_linker = 1
=head3 Put makepp extensions into a module
Makepp offers very convenient possibilities of being extended through Perl.
But
if
you
write
some functions, commands or statements in a file and include
that from dozens of makefiles, you will get dozens of copies of them all in
memory. And they will be
read
dozens of
times
by the makepp parser, which is
a bit slower than perl's.
In this situation it is better to L<put your own functions into a
module|makepp_extending/Putting functions into a Perl module>.
=head3 Use Repositories and/or a Build Cache
If you have several developers working on the same machine or
if
you change to
and fro between sets of build options, this is
for
you.
L<Repositories|makepp_repositories> allow you to offer a central reference
where you only need to build what is locally different. A L<build
cache|makepp_build_cache> simply collects all produced files, and reuses them
as appropriate,
with
less planning needed. The latter page also describes the
differences.
=head3 Use Sandboxes
If your build is so big that makepp is having a hard
time
digesting all the
information and
if
you can find a way of splitting it up into smaller
independent parts, L<sandboxes|makepp_sandboxes> might give you better
parallelity than the C<--jobs> option.
=head3 Don't
log
what you
do
Makepp's logging feature is very powerful
for
tracking down bugs in the build
system
, or
for
analyzing your dependencies. Whenever you don't
do
these
things, you can save quite a bit of formatting and I/O
with
C<--
no
-
log
--
no
-scan-
log
>.
=head2 Almost Safe Methods
=head3 Get a Headstart
The option C<--stop-
after
-loading> (or just C<--stop>) allows makepp to start
its work
while
you are still editing. It will suspend itself
when
it gets to
the point analyzing the dependencies. You decide
when
you're ready to let it
go on. On
our
huge project this saves half a minute, and that's only
when
we
have a CPU to ourselves.
This method
has
two potential drawbacks:
=over
=item *
Makeppfiles have been
read
by the
time
makepp stops. If you edit a Makeppfile
or something from which it would have to be rebuilt,
after
starting makepp,
this will go unnoticed till the
next
time
. But this should rarely be
necessary, since makepp greatly reduces the need
for
Makeppfile changes.
=item *
If a target depends on a wildcard, and that would match more than
when
the
Makeppfile was
read
, makepp will not notice:
proggie: *.o
$(LD) $(inputs) -o $(output)
If you add another source file, or a file from which makepp knows how to
generate a source, then C<*.o> should match the object that produces. But,
if
this file was added
after
starting makepp, it will not, because the wildcard
was expanded too early.
=back
In both of these cases you should
kill
the prestarted makepp and start it anew.
You can
do
something like the following in your Shell's
$ENV
file or .profile
to save typing (csh users replace
'='
with
' '
):
alias mpps=
'makepp --stop'
=head3 Gulliver's Travels
The option C<--gullible> tells makepp to believe that a rule changes what it
says it will, neither less nor more. Not performing these checks can save a
few percent of makepp's CPU
time
. And the Disk I/O savings is especially
welcome on network file systems. If you
do
nightly full builds in an empty
directory
with
the C<--repository> option, but without the C<--gullible>
option, you can be fairly sure that your rule set is consistent. Then this
option shouldn't hurt in your daytime work.
=head2 Potentially Unsafe Methods
These methods are unsafe
if
you give makepp the wrong hints. But everything
will again be fine, however, as soon as you let makepp
do
all the checks, by
not passing it any limiting options. For this reason I suggest using these
hints to get quick intermediate builds, and
use
lunchtime and nights to let
makepp
do
its job thoroughly.
=head3 Build as Little as Needed
This is the same tip of using explicit targets discussed under L<Build as
Little as You Need> above. But it becomes more dangerous,
if
you
do
it
because you are sure that your change will not affect any of the other
programs. Then they will not be built, even though it might have been
necessary.
=head3 Know Where Not to Build
The option C<--dont-build> is very powerful
for
speeding makepp up a lot. If
you know one or more directories, which you are sure are unaffected by any
change you made since the
last
time
, you can issue C<--dont-build> options
for
them. This can save makepp a lot of dependency analysis. But it will not
build anything in those directories, even
if
it should have.
=head3 Know Where to Build
This is the same as L<Know where not to build>, but instead of an exclusion
list, you supply an inclusion list. The trick is that a C<--
do
-build> option,
with
a C<--dont-build=/> option or under a C<RootMakeppfile(.mk)> directory
without a C<--dont-build> option on a higher level directory means: build
nothing except what I
tell
you to. This is what users of traditional makes
are looking
for
when
they want to build just one directory:
$ makepp --
do
-build=dir/subdir
or,
if
you don't have a C<RootMakeppfile(.mk)>:
$ makepp --dont-build=/ --
do
-build=dir/subdir
The difference is that any
default
target in the top level Makeppfile,
i.e.
link
commands are also executed this way. If you don't want that, you
must give an explicit target, which is automatically also marked
for
C<--
do
-build>:
$ makepp --
do
-build=dir1/subdir dir2/proggie
=head3 Know What to Build
An extreme variant is asking makepp not to build anything but what you
tell
it
to. This is not so dangerous
if
you changed B<
no
include files>, only
modules, and you know which programs they go into.
Say you have only changed C<src/a.cpp> and C<src/b.cpp> and these are linked
directly into one program. Dot is the current directory including all
subdirectories.
$ makepp --dont-build=. src/a.o src/b.o proggie1
Or equivalently, because a C<--
do
-build> option, without a C<--dont-build>
option on a higher level directory implies C<--dont-build>
for
the root of the
build tree:
$ makepp --
do
-build=src/a.o src/b.o proggie1
You can
do
something like the following in your Shell's
$ENV
file or .profile
to save typing (csh users replace
'='
with
' '
):
alias mppb=
'makepp --do-build'
alias mppsb=
'makepp --stop --do-build'
Then the
last
example becomes:
$ mppb src/a.o src/b.o proggie1
=head3 Build on a RAM disk
Modern computers, especially servers, typically have a high mean
time
between
failure. If this is the case
for
you, and you have lots of RAM to spare, you
can save the
time
you
wait
for
I/O. You should edit on a real disk, or
replicate your edits there quickly. But the build results are reproducible,
so they can reside in RAM. If you don't want to risk rebuilding, you can
always replicate to disk
after
each
build or at night. You should not
do
this
during the build, as you might
catch
partially written files, just as
if
the
machine had crashed.
If you have a
system
and/or storage unit
with
good caching and RAID, the gain
might not be so big.
=head1 AUTHOR
Daniel Pfeiffer <occitan
@esperanto
.org>