我正在用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),然后将结果相加。例如,给定i3值:
 3 << 2 == 12
 3 + 12 == 15
15 + 15 == 30

如果您更改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

此版本使用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/

10-11 18:49