我正在用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));
}
现在,我想通过在常数值不同的情况下运行几次来对该程序进行实验。我想自动执行此操作,而不是每次更改CONSTANT时都不要重新编译。我使用了一个简单的解决方案,将宏更改为全局变量,将原始主函数放入新的``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;
}
我发现此实现导致相当大的循环性能损失
for(int i = 1; i< 10; i++)
printf("%d\n",multiplication_by_constant(i));
与使用宏相比,这现在需要大约50%的运行时间。这正常吗?
有人可以建议我进行实验的更有效方法吗?
PS在C++中,我将通过定义“类程序”,将CONSTANT设置为类成员,并将“multiplication_by_constant”设置为成员函数来实现。然后,我可以使用类定义轻松地通过“main”进行实验,而且我认为不会造成效率损失...在此实现中必须使用C,这就是为什么我诉诸于全局变量和功能。
最佳答案
好吧,这是性能差异的潜在来源。使用宏,在编译时就知道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
突出显示的行是函数的内容;该函数不会将
%eax
中的值乘以10,而是进行算术左移2位,然后加法(有效乘以5),然后将结果相加。例如,给定i
的3
值: 3 << 2 == 12
3 + 12 == 15
15 + 15 == 30
如果您更改
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
此版本使用
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
然后您的C代码会
#include
const.h文件:#include "const.h"
int multiplication_by_constant(int a){ return a*CONSTANT;}
...
1.平台是SUSE Linux Enterprise Server 10(x86_64),编译器是gcc 4.1.2
关于c - 宏比C中的全局变量运行快吗?如何在运行之间更改宏?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30403270/