简体   繁体   中英

Do any C or C++ compilers optimize within define macros?

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 (ie 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?

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.

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.

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.)

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.

#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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM