在定义宏中是否优化任何C或C++编译器?

msh*_*ang 9 c c++ compiler-optimization c-preprocessor

假设我在C或C++中有以下内容:

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

然后,我去使用NODESA_CONSTANT内许多嵌套循环深的地方(即多次使用).显然,两者都有可以在编译时确定的数值,但编译器实际上是这样做的吗?在运行时,CPU是否必须在15*16每次看到时进行评估NODES,还是编译器静态放置240?同样,CPU每次看到时都必须评估平方根A_CONSTANT吗?

我的猜测是ROWS*COLS乘法被优化了,但没有别的.整数乘法内置于语言中,但sqrt是一个库函数.如果确实是这种情况,有没有办法得到一个等效的幻数,以便A_CONSTANT在运行时只计算一次平方根?

Fre*_*Foo 20

宏定义通过简单的文本替换扩展到源代码中,然后再转交给编译器,这可能会进行优化.编译器将为表达式生成完全相同的代码NODES,ROWS*COLS并且15*16(我无法想到每次循环启用优化时都会进行乘法的单个代码).

至于A_CONSTANT它再次成为一个宏的事实并不重要; 重要的是编译器是否足够聪明,以确定sqrt常量是一个常量(假设是sqrt来自<math.h>).我知道GCC足够聪明,我希望其他生产质量的编译器也足够聪明.


mir*_*ith 11

#define中的任何内容都作为预编译步骤插入到源代码中,这意味着一旦编译代码,宏基本上就消失了,代码就像往常一样编译.是否优化取决于您的代码,编译器和编译器设置.


Nem*_*emo 9

这取决于你的编译器.

#include <math.h>

#define FOO sqrt(5);

double
foo()
{
  return FOO;
}
Run Code Online (Sandbox Code Playgroud)

我的编译器(gcc 4.1.2)为此代码生成以下程序集:

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

所以它确实知道这sqrt(5)是一个编译时常量.

如果您的编译器不那么聪明,我不知道在编译时计算平方根的任何可移植方法.(当然,您可以计算一次结果并将其存储在全局或其他内容中,但这与编译时常量不同.)

  • @Heath:你在开玩笑吧?我回答了他问的问题(即,我读了它),而不仅仅是基于他在标题中如何表达它.如果我确定他的头衔以更好地反映他的问题,你会删除你的downvote吗? (4认同)
  • 无论如何,以你不认为它回答问题为由拒绝接受*答案似乎是荒谬的.游戏的目标,如果它必须是一个游戏,就是回答提问者想要的内容,而不是回答提问者意外打错的内容,或者由于不知道他们需要知道什么而错误地询问,以便清楚地提出他们的问题.这只回答了问题的第二部分,但它确实正确. (2认同)

Cra*_*rks 8

这里真的有两个问题:

  1. 编译器是否优化了宏中的表达式?
  2. 编译器是否优化sqrt()

(1)很容易:是的,确实如此.预处理器与C编译器分开,并在C编译器启动之前完成它.所以,如果你有

#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);
   }
}
Run Code Online (Sandbox Code Playgroud)

编译器实际上会看到:

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);
   }
}
Run Code Online (Sandbox Code Playgroud)

这受到所有通常的编译时常量优化的影响.

sqrt()更棘手,因为它从编译器到编译器各不相同.在大多数现代编译器中,sqrt()实际上是编译器内部函数而不是库函数 - 它看起来像一个函数调用,但它实际上是编译器中的一个特殊情况,它具有基于数学定律,硬件操作等的附加启发式算法.在sqrt()这种特殊情况下的智能编译器中,sqrt()常量值将在内部转换为常数.在愚蠢的编译器中,每次都会产生一个函数调用.知道你得到的唯一方法是编译代码并查看发出的程序集.

从我所看到的情况来看,MSVC,现代GCC,英特尔,IBM和SN都是sqrt内置的.旧的GCC和一些蹩脚的供应商提供的嵌入式芯片编译器没有.