c - 在 C 语言中,宏是否比全局变量运行得更快?如何在运行之间更改宏?

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

现在我想通过使用不同的常量值多次运行程序来对程序进行实验。我想自动执行此操作,而不是每次更改 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<em><strong>
14 000c C1E002                sall    $2, %eax
15 000f 01D0                  addl    %edx, %eax
16 0011 01C0                  addl    %eax, %eax</strong></em>
17 0013 C9                    leave
18 0014 C3                    ret

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

 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<strong><em>
20 000d 0FAF45FC              imull   -4(%rbp), %eax</em></strong>
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/

相关文章:

c - 为什么这些构造使用增量前和增量后未定义的行为?

java - 在 JNI 实现中创建静态全局变量不好吗?

macros - 如何在低级别拦截并纠正按键?

c++ - 使用 MACRO 在 for 循环中动态创建字符串(例如数组索引)

c++ - 使用宏访问指向数据成员的指针会产生 "error: expected unqualified-id before ‘*’ token ”

C 将 emacs 中的十六进制值转换为不正确的值

c - a 和 b 的值是多少?

c - 如何在 if-else 语句中使用多个 execvp 调用?

用于日期的 Xcode 预处理器宏

C++ 循环重复代码