宏中的宏运行速度比C中的全局变量快吗?如何在运行之间更改宏?

Pla*_*men 2 c macros

我正在用C语言编写一个程序,其中有几个常量我希望我的所有函数都可以使用.到目前为止,我已经使用了宏.该程序的简化版本如下所示.

#define CONSTANT 10 //int
int multiplication_by_constant(int a){ return a*CONSTANT;}
int main(){
   for(int i = 1; i< 10; i++)
       printf("%d\n",multiplication_by_constant(i));
}
Run Code Online (Sandbox Code Playgroud)

现在我想通过使用不同的常量值运行几次来对程序进行实验.我想自动执行此操作,而不是每次更改CONSTANT时都不重新编译.我使用一个简单的解决方案,将宏更改为全局变量,将原始main函数放入新的'program()'函数,然后在主文件中运行实验,即

int CONSTANT =  10; //int
int multiplication_by_constant(int a){ return a*CONSTANT;}
void program(){
   for(int i = 1; i< 10; i++)
       printf("%d\n",multiplication_by_constant(i));
}
int main(){
   while(CONSTANT < 100){
       program();
   }
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

我发现这种实现导致了循环性能的相当大的损失

for(int i = 1; i< 10; i++)
       printf("%d\n",multiplication_by_constant(i));
Run Code Online (Sandbox Code Playgroud)

现在比使用宏时需要大约50%的运行时间.这是正常的吗?

任何人都可以建议我更有效地运行我的实验吗?

PS在C++中,我会通过定义一个'类程序',将CONSTANT设置为类成员并将'multiplication_by_constant'设置为成员函数来实现.然后我可以使用类定义轻松地通过'main'运行实验,我想我不会有效率损失......我必须在这个实现中使用C,这就是为什么我使用了全局变量和功能.

Pol*_*k-Z 5

确切地说,宏不会"运行".宏是对C预处理器的指令,它在将宏传递给编译器之前有效地将宏的使用替换为宏的定义.给定的宏或函数调用是否更有效取决于宏和函数调用.在你的例子中,正如John Bode在他的回答中所示,让宏表示文字比使用变量更有利.一般来说,在内联和宏之间表达相同的代码之间没有固有的区别.更有效的编码内联语句将比效率更低的编码宏更有效.


Joh*_*ode 5

好吧,这是性能差异的潜在来源。对于宏, 的值CONSTANT编译时是已知的,因此编译器可以利用该知识并以稍微不同的方式构造机器代码。我采用了您的代码的变体,并用于gcc -Wa,-aldh获取宏和全局变量版本的程序集列表1,其中有一个有趣的区别。

首先是宏版本:


 3                    .globl mul_by_const
 5                    mul_by_const:
 6                    .LFB2:
 7 0000 55                    pushq   %rbp
 8                    .LCFI0:
 9 0001 4889E5                movq    %rsp, %rbp
10                    .LCFI1:
11 0004 897DFC                movl    %edi, -4(%rbp)
12 0007 8B55FC                movl    -4(%rbp), %edx
13 000a 89D0                  movl    %edx, %eax
14 000c C1E002                sall    $2, %eax
15 000f 01D0                  addl    %edx, %eax
16 0011 01C0                  addl    %eax, %eax
17 0013 C9                    leave
18 0014 C3                    ret
Run Code Online (Sandbox Code Playgroud)

突出显示的行是函数的核心;%eax该函数不是将 in 的值乘以10,而是将算术左移 2 位,然后进行加法(有效地乘以 5),然后将结果与自身相加。例如,给定一个i3

 3 << 2 == 12
 3 + 12 == 15
15 + 15 == 30
Run Code Online (Sandbox Code Playgroud)

如果更改 的值CONSTANT,编译器将生成不同的机器代码(CONSTANT7导致算术左移 3 后进行减法,而值19给出左移 3 后进行 3 次加法等)。

将其与全局变量版本进行比较:


 2                    .globl CONSTANT
 3                            .data
 4                            .align 4
 7                    CONSTANT:
 8 0000 0A000000              .long   10
 9                            .text
10                    .globl mul_by_const
12                    mul_by_const:
13                    .LFB2:
14 0000 55                    pushq   %rbp
15                    .LCFI0:
16 0001 4889E5                movq    %rsp, %rbp
17                    .LCFI1:
18 0004 897DFC                movl    %edi, -4(%rbp)
19 0007 8B050000              movl    CONSTANT(%rip), %eax
19      0000
20 000d 0FAF45FC              imull   -4(%rbp), %eax
21 0011 C9                    leave
22 0012 C3                    ret
Run Code Online (Sandbox Code Playgroud)

此版本使用imull操作码进行乘法运算。由于CONSTANT在编译时不知道的值,因此编译器无法根据该值进行任何特殊优化。

现在,这就是在我的特定平台上发生的事情;我不知道您正在使用什么编译器或您正在运行什么操作系统,因此您获得的机器代码可能与我上面的不同。我只是指出CONSTANT在编译时知道允许编译器做一些额外的优化。

编辑

如果更改宏的值,必须重新编译。您可以将宏放在头文件中,然后编写一个脚本来重新生成头文件并重新编译,例如

#!/bin/bash

let CONSTANT=1
while [ $CONSTANT -lt 20 ]
do
  cat > const.h << EOF
#ifndef CONST_H
#define CONST_H
#define CONSTANT $CONSTANT
#endif
EOF
  # build and run your test code, assuming profiling is captured
  # automatically
  gcc -o test test.c
  ./test
  let CONSTANT=CONSTANT+1
done
Run Code Online (Sandbox Code Playgroud)

你的 C 代码将#include是 const.h 文件:

#include "const.h"
int multiplication_by_constant(int a){ return a*CONSTANT;}
...
Run Code Online (Sandbox Code Playgroud)


1.平台为SUSE Linux Enterprise Server 10 (x86_64),编译器为gcc 4.1.2