繁体   English   中英

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

[英]Do any C or C++ compilers optimize within define macros?

假设我在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)))

然后,我 go 并在许多嵌套循环的深处使用NODESA_CONSTANT (即多次使用)。 显然,两者都有可以在编译时确定的数值,但编译器真的这样做了吗? 在运行时,CPU 每次看到NODES时都必须评估15*16 ,还是编译器会静态地将240放在那里? 同样,CPU 每次看到A_CONSTANT时都必须计算平方根吗?

我的猜测是ROWS*COLS乘法被优化了,但没有别的。 Integer 乘法内置在语言中,但 sqrt 是一个库 function。 如果确实如此,有没有办法获得一个相当于A_CONSTANT的幻数,以便在运行时只计算一次平方根?

在将宏定义传递给适当的编译器之前,通过简单的文本替换到源代码中来扩展宏定义,这可能会进行优化。 编译器将为表达式NODESROWS*COLS15*16生成完全相同的代码(而且我想不出一个会在启用优化的情况下每次循环执行乘法的单个代码)。

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

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

这取决于你的编译器。

#include <math.h>

#define FOO sqrt(5);

double
foo()
{
  return FOO;
}

我的编译器(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:

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

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

这里真的有两个问题:

  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);
   }
}

编译器实际上会看到:

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);
   }
}

这受制于所有通常的编译时常量优化。

sqrt()比较棘手,因为它因编译器而异。 在大多数现代编译器中,sqrt() 实际上是编译器内在而不是库 function — 它看起来像 function 调用,但它实际上是编译器内部的一个特殊情况,它具有基于数学定律、硬件操作等的额外启发式方法。在sqrt()是这种特殊情况的智能编译器中,常量值的sqrt()将在内部转换为常量。 在愚蠢的编译器中,每次都会导致 function 调用。 知道你得到的唯一方法是编译代码并查看发出的程序集。

据我所见,MSVC、现代 GCC、英特尔、IBM 和 SN 都将sqrt视为 intrinisc。 旧的 GCC 和一些糟糕的供应商提供的嵌入式芯片编译器没有。

#defines在编译处理,简单的文本替换。 然后将生成的文本文件传递给实际的编译步骤。

如果您使用的是 gcc,请尝试使用-E开关编译源文件,这将执行预处理然后停止。 查看生成的文件以查看编译步骤的实际输入。

宏将被替换,然后像代码的rest那样编译代码。 如果您开启了优化(并且您使用的编译进行了不错的优化),您可能会期望在编译时计算出这样的事情。

从这个角度来看,C++ 编译器的年代相对较少,以至于您认为它们缺乏这样的优化。 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我记得,在 CP/M 上运行的 C 编译器大多没有。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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