我正在用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,这就是为什么我使用了全局变量和功能.
确切地说,宏不会"运行".宏是对C预处理器的指令,它在将宏传递给编译器之前有效地将宏的使用替换为宏的定义.给定的宏或函数调用是否更有效取决于宏和函数调用.在你的例子中,正如John Bode在他的回答中所示,让宏表示文字比使用变量更有利.一般来说,在内联和宏之间表达相同的代码之间没有固有的区别.更有效的编码内联语句将比效率更低的编码宏更有效.
好吧,这是性能差异的潜在来源。对于宏, 的值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),然后将结果与自身相加。例如,给定一个i值3:
3 << 2 == 12
3 + 12 == 15
15 + 15 == 30
Run Code Online (Sandbox Code Playgroud)
如果更改 的值CONSTANT,编译器将生成不同的机器代码(CONSTANT值7导致算术左移 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)