10

Let's say I have the following in C or C++:

#include <math.h>
#define ROWS 15
#define COLS 16
#define COEFF 0.15
#define NODES (ROWS*COLS)
#define A_CONSTANT (COEFF*(sqrt(NODES)))

Then, I go and use NODES and A_CONSTANT somewhere deep within many nested loops (i.e. used many times). Clearly, both have numeric values that can be ascertained at compile-time, but do compilers actually do it? At run-time, will the CPU have to evaluate 15*16 every time it sees NODES, or will the compiler statically put 240 there? Similarly, will the CPU have to evaluate a square root every time it sees A_CONSTANT?

My guess is that the ROWS*COLS multiplication is optimized out but nothing else is. Integer multiplication is built into the language but sqrt is a library function. If this is indeed the case, is there any way to get a magic number equivalent to A_CONSTANT such that the square root is evaluated only once at run-time?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
mshang
  • 955
  • 4
  • 11
  • 18
  • You could store the constant in a global variable (and if you're using C++, you could also use `const` to ensure that it is constant). Then it will be evaluated once, globally (only at runtime though). – Platinum Azure Jul 22 '11 at 21:32
  • 3
    All the macros have been replaced by the pre-processor long before the compiler even sees the source. – Martin York Jul 22 '11 at 22:12

6 Answers6

22

Macro definitions are expanded by simple textual substitution into the source code before it's handed to the compiler proper, which may do optimization. A compiler will generate exactly the same code for the expressions NODES, ROWS*COLS and 15*16 (and I can't think of a single one that will do the multiplication every time round the loop with optimization enabled).

As for A_CONSTANT, the fact that it is a macro again doesn't matter; what matters is whether the compiler is smart enough to figure out that sqrt of a constant is a constant (assuming that's sqrt from <math.h>). I know GCC is smart enough and I expect other production-quality compilers to be smart enough as well.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
12

Anything in a #define is inserted into the source as a pre-compile step which means that once the code is compiled the macros have basically disappeared and the code is compiled as usual. Whether or not it is optimized depends on your code, compiler and complier settings.

mirswith
  • 1,285
  • 1
  • 10
  • 15
9

It depends on your compiler.

#include <math.h>

#define FOO sqrt(5);

double
foo()
{
  return FOO;
}

My compiler (gcc 4.1.2) generates the following assembly for this code:

.LC0:
    .long   2610427048
    .long   1073865591
    .text
    .p2align 4,,15
.globl foo
    .type   foo, @function
foo:
.LFB2:
    movsd   .LC0(%rip), %xmm0
    ret
.LFE2:

So it does know that sqrt(5) is a compile-time constant.

If your compiler is not so smart, I do not know of any portable way to compute a square root at compile time. (Of course, you can compute the result once and store it in a global or whatever, but that is not the same thing as a compile-time constant.)

Nemo
  • 70,042
  • 10
  • 116
  • 153
  • 1
    Note that if the compiler can prove that the function is "const" (in this case, doesn't modify any globals and has the same output for the same input), it can then hoist the constant out of the loop. So `double result = 0; for (unsigned i = 0; i < rand() % 10; ++i) result += FOO;` can have the loop statement converted to: `result += 2.23606798;`. More information on what compilers can do this in principle here: http://stackoverflow.com/questions/2798188/pure-const-function-attributes-in-different-compilers – GManNickG Jul 22 '11 at 21:40
  • -1: Whether the optimizer operates on code from within #define'd macros is independent of the compiler. As long as it contains an optimizer, the answer is yes; otherwise there is the trivial 'no' case of a compile which doesn't have an optimizer. Only in that sense does the question posed in the title "depend" on the compiler. – Heath Hunnicutt Jul 22 '11 at 21:49
  • 4
    @Heath: You're kidding, right? I answered the question he asked (i.e., I _read_ it), not just based on how he phrased it in the title. If I fix his title to reflect his question better, will you remove your downvote? – Nemo Jul 22 '11 at 21:52
  • No, I'm not kidding. Changing OP's title would give the impression that his intention was other than what it originally appeared to be. Other answers, such as larsmans', address the "issue" of #define -- which the OP asked about in his question as OP wrote it. – Heath Hunnicutt Jul 22 '11 at 22:06
  • il va sans dire, if a compiler can optimize `sqrt(5)`, it can optimize constants into defines (even more easily if they become just `5*16`). Or we should think the preamble to this question is "the OP does not know what the preprocessor does". — basically no reason to downvote – ShinTakezou Jul 22 '11 at 22:11
  • "I do not know of any portable way to compute a square root at compile time" - in C++, it's an easy TMP job, you don't need many levels of recursion to compute a reasonable sqrt. It won't necessarily be exactly the value that `sqrt` gives at runtime, of course. Gives you a constant expression, but non-integer constant expressions aren't much use anyway as far as the language is concerned. *Might* help the compiler. – Steve Jessop Jul 22 '11 at 22:12
  • 2
    Seems absurd anyway to downvote an *accepted* answer on the grounds that you don't think it answers the question asked. The object of the game, if it must be a game, is to answer what the questioner wants, not to answer what the questioner accidentally typoed, or mistakenly asked due to not knowing what they need to know in order to ask their question clearly. This does only answer the second part of the question, but it does so correctly. – Steve Jessop Jul 22 '11 at 22:14
  • @Steve: I thought you couldn't use floating point numbers as template parameters? Or maybe you are envisioning some fixed-point implementation? Actually, this would not be a bad question in its own right :-) – Nemo Jul 22 '11 at 22:40
  • 1
    @Nemo: Not as template parameters, no, so a general TMP sqrt function would be awkward, I suppose it would have to take as parameter a type having a static member with the value. But you can define `template struct ApproximationToSqrtOfNODES { ... };`, containing successive approximations to the sqrt of NODES, which is easy. Takes my compiler 8 steps of Newton's method starting from `NODES/2` to get a `double` value equal to `sqrt(240)`. – Steve Jessop Jul 22 '11 at 22:44
8

There's really two questions here:

  1. Does the compiler optimize expressions found inside macros?
  2. Does the compiler optimize sqrt()?

(1) is easy: Yes, it does. The preprocessor is seperate from the C compiler, and does its thing before the C compiler even starts. So if you have

#define ROWS 15
#define COLS 16
#define NODES (ROWS*COLS)

void foo( ) 
{
   int data[ROWS][COLS];
   printf( "I have %d pieces of data\n", NODES );
   for ( int *i = data; i < data + NODES ; ++i )
   {
     printf("%d ", *i);
   }
}

The compiler will actually see:

void foo( ) 
{
   int data[15][16];
   printf( "I have %d pieces of data\n", (15*16) );
   for ( int *i = data; i < data + (15*16) ; ++i )
   {
     printf("%d ", *i);
   }
}

And that is subject to all the usual compile-time constant optimization.

sqrt() is trickier because it varies from compiler to compiler. In most modern compilers, sqrt() is actually a compiler intrinsic rather than a library function — it looks like a function call, but it is actually a special case inside the compiler that has additional heuristics based on mathematical laws, hardware ops, etc. In smart compilers where sqrt() is such a special case, sqrt() of a constant value will be translated internally to a constant number. In stupid compilers, it will result in a function call each time. The only way to know which you're getting is to compile the code and look at the emitted assembly.

From what I've seen, MSVC, modern GCC, Intel, IBM, and SN all handle sqrt as intrinisc. Old GCC and some crappy vendor-supplied compilers for embedded chips do not.

Crashworks
  • 40,496
  • 12
  • 101
  • 170
3

#defines are handled before compilation, with simple text replacement. The resulting text file is then passed to the actual compilation step.

If you are using gcc, try compiling a source file with the -E switch, which will do the preprocessing and then stop. Look at the generated file to see the actual input to the compilation step.

Anders Abel
  • 67,989
  • 17
  • 150
  • 217
1

The macro will be substituted, and then the code compiled like the rest of the code. If you've turned on optimization (and the compiler you're using does decent optimization) you can probably expect things like this to be computed at compile time.

To put that in perspective, there are relatively few C++ compilers old enough that you'd expect them to lack optimization like that. Compilers old enough to lack that simple of optimization will generally be C only (and even then, don't count on it -- definitely things like MS C 5.0/5.1/6.0, Datalight/Zortech C, Borland, etc., did this as well. From what I recall, the C compilers that ran on CP/M mostly didn't though.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111