[Top][All Lists]

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

Re: use of math.h / libmath discouraged?

From: Brian Vandenberg
Subject: Re: use of math.h / libmath discouraged?
Date: Wed, 25 Jul 2018 14:25:56 -0600

However, I'm not sure that this function is sufficiently generic to be
added as a built-in function.  Can you provide use-cases for it?

I suppose that depends on the definition of "sufficiently generic".  The two definitions that come to mind:

  1. Useful for many people who do build maintenance
  2. Useful for people trying to improve performance on large builds or on makefile "libraries" (eg gmsl or libmakefile)

If the chosen definition is (1) then you're right, but I think it's safe to say most people in (2) would find it useful (or would have).

I'll try to give some good examples, though I came up with this years ago and I'm just getting around to contributing it.


One of the more annoying problems I've encountered when writing canned expressions is whitespace creeping in.  Here's a contrived example with a potential solution:

$ cat makefile
F1 = ${${1}}
F2 = $(call ${0}_,$(strip ${1}))
F2_ = ${${1}}
TEXT = something
# note: the space before the word TEXT is helpful for readability but causes a problem
$(info $(call F1, TEXT))
$(warning $(call F2, TEXT))
$ make --warn-undefined-variables
makefile:6: warning: undefined variable ' TEXT'
makefile:7: something

Adding that extra level of indirection makes it easier to write the _expression_ without worrying about unexpected whitespace but there's a cost that's easy to ignore if you don't have a straightforward way to measure it.


One of the bottlenecks I uncovered in our build looked something like this:

> ${CC} ${SOMETHING_EXPENSIVE} ${OTHER_FLAGS} ${^} -o address@hidden

The documentation says it but I didn't fully appreciate it until diving into this problem: the entire recipe undergoes expansion before a subshell gets created (either that or it occurs after vfork() and before execve(), but the effect is still the same).  The non-obvious [to me] consequence: expensive operations in a recipe can artificially limit the number of jobs make can spawn/reap since time spent on recipe expansion is time not spent reaping/spawning jobs.  The impact isn't as bad if the build is recursive, but that doesn't apply to us.


After I created the timeit function I spent a few days coming up with / looking for awful contrivances to help me better understand what effect various expressions might have on time spent parsing them.  Here's a few I remember, though I cannot guarantee these are functional as written -- I'm writing from memory without testing them:

# Recursion:
F1 = $(strip $(if $(filter 16,${3}),${2},$(call ${0},${1},${${1}} ${2},$(words ${2}))))
TEST = something
# prints the word "something" 16 times
$(info $(call F1,TEST,,))

# Automatically generate a target
$(eval ${1}.o := $(addsuffix .o, $(basename $(wildcard ${2}))))
# evals needed because of ${${1}.o}
$(eval ${1} : ${${1}.o})
# ... whatever other strange contrivance comes to mind ...

# recursively expanded variable (B) with partial expansion.
A = a
B = ${A}
$(eval B=$(value B) ${B})
# prints: ${A} a
$(info $(value B))

# variable reflection
THING = $(call ${0}_,$(filter $(firstword $(notdir $(basename ${1}))).$(strip ${2}),${.VARIABLES}),$(strip ${2}))
THING_ = ${$(strip $(if ${1},${1},${2}))}
# Depending on its existence either bar.CFLAGS or CFLAGS gets evaluated
bar : ; ${CC} $(call THING, address@hidden, CFLAGS) ${^} -o address@hidden

# Conditional evaluation of macro arguments; ifdef/ifndef is from our plugin 
F = $(ifndef 3,$(error ${0} requires 3 arguments))...stuff...
Testing the effect they have on performance could be tricky.  Without a way to evaluate timing of specific expressions you're limited to:

  1. Build make / attach a profiler / figure out how to attribute performance changes to expressions used in the makefile
  2. Use time (or something similar) / establish a baseline by removing the code in question
  3. Cook up some makefile hackery that in some way measures time spent evaluating the _expression_

(1) is probably not realistic.  With (2): if _expression_ A causes _expression_ B to have performance problems, but B is in your baseline then the performance loss will be attributed to A whereas the problem may lie in B.

Without a builtin or plugin function, (3) amounts to something like this:

NOW = $(shell date +%s%N)
before := ${NOW}
after := ${NOW}
$(info difference: $(shell expr ${after} - ${before}))

... which has limitations:

  • Relies on $(shell) which may skew/bias the results (though to be fair a builtin/plugin function could be added that returns a high resolution timestamp)
  • Would be more complicated to apply this to recipe parsing, .SECONDEXPANSION and other situations where temporary variables may be an issue, though you could get around it with something like: $(info ${NOW})${_expression_}$(info ${NOW}) and post-process the results

Anyway, my diarrhea of the keyboard should probably end before I think of more to write.


reply via email to

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