大型switch语句的宏是否比具有大型switch语句的函数更快?

时间:2017-11-01 23:21:53

标签: c macros

因此,对于内联函数(1-2个语句)和小型宏,似乎在使用宏或内联函数之间没有太大的性能差异。

但是,考虑到更大函数的函数调用开销,我想知道,

  • GCC 7.0
  • C语言(不是C ++)
  • OSX(不确定这是否会改变那么多跨平台)

对switch语句使用大型宏会比将它们置于等效的函数调用中更快吗?这是我的假设,不会内联这样大的功能。这是我的示例代码。

#define LEX_CHAR(chPtr, tag) switch(*chPtr) { \
    case 'a':\
    case 'b':\
    case 'c':\
    case 'e':\
    case '$': tag = Tag_A;\
              break; \
    case '0':\
    case '1':\
    case '2':\
    case '3': tag = Tag_B;\
             break;\
    case 'r':\
          if(chPtr[1] == 'd' || chPtr[1] == '@') tag = Tag_c;\
          else tag = Tag_B;\
          break;\
    case '+':\
    case '#':\
    case '!':\
         if(chPtr[1] == 'd') tag = Tag_C;\
    case '-':\
    case '^':\
            tag = Tag_D;\
            break;\
    default:\
            tag = Tag_B;\
}

enum Tag
{
  Tag_A,
  Tag_B,
  Tag_C,
  Tag_D
};

typedef enum Tag Tag;


void Lex_Char(char* chPtr, Tag* tag)
{
switch(*chPtr) { 
    case 'a':
    case 'b':
    case 'c':
    case 'e':
    case '$': *tag = Tag_A;
              break; 
    case '0':
    case '1':
    case '2':
    case '3': *tag = Tag_B;
             break;
    case 'r':
          if(chPtr[1] == 'd' || chPtr[1] == '@') *tag = Tag_C;
          else *tag = Tag_B;
          break;
    case '+':
    case '#':
    case '!':
         if(chPtr[1] == 'd') *tag = Tag_C;
    case '-':
    case '^':
            *tag = Tag_D;
            break;
    default:
            *tag = Tag_B;
     }  
}

所以在这两者之间,宏和函数,在函数上使用宏有什么优化吗?

2 个答案:

答案 0 :(得分:1)

首先,请注意,当您在宏中有代码时,编译器必须在调用代码中内嵌它。当您将其设为函数时,编译器可以将其插入内联。

您还应该了解在声明如下函数时会发生什么:

void Lex_Char(char* chPtr, Tag* tag) { ... }

这告诉编译器可以从其他C文件访问该函数 - 编译器必须创建此函数的完整版本。内联函数将意味着制作两个代码副本 - 一个用于完整功能版本,另一个在调用站点内联。编译器将不愿意这样做,除非您的优化设置强调大小超速。

如果只在当前翻译单元中使用某个功能,则应将其标记为“静态”:

static void Lex_Char(char* chPtr, Tag* tag) { ... }

这告诉编译器它无法从外部访问。如果该函数只在当前模块中使用过一次,那么编译器可以愉快地内联它 - 这样做是“免费的”。

您还可以将该函数标记为“静态内联”,为编译器提供一个暗示您热衷于内联的提示。

当然,这一切都取决于是否为编译器启用了优化 - 如果您不启用优化,那么您所有的时间测试都毫无价值。

内联静态函数总是比宏更好的选择(当你有选择时 - 宏可以比内联函数更灵活)。代码更清晰,您可以更好地进行静态警告和错误检查。结果代码(假设优化)将是相同的。

顺便提一下,你的时序测试在这里毫无意义 - 编译器会看到所涉及的值不会改变,并且在内联并启用优化时不会多次运行该函数。它可能根本不运行,但在编译时预先计算结果。

哦,你忘了案件中的“休息”'!'。

答案 1 :(得分:0)

因此,经过定时测试后,在相同的for循环下重复,宏版本的速度大约是常规函数的两倍。

这里是我的完整计时器和编译完整文件以生成结果

#include "stdio.h"
#include "stdlib.h"
#include "time.h"



#define LEX_CHAR(chPtr, tag) switch(*chPtr) { \
    case 'a':\
    case 'b':\
    case 'c':\
    case 'e':\
    case '$': tag = Tag_A;\
              break; \
    case '0':\
    case '1':\
    case '2':\
    case '3': tag = Tag_B;\
             break;\
    case 'r':\
          if(chPtr[1] == 'd' || chPtr[1] == '@') tag = Tag_C;\
          else tag = Tag_B;\
          break;\
    case '+':\
    case '#':\
    case '!':\
         if(chPtr[1] == 'd') tag = Tag_C;\
    case '-':\
    case '^':\
            tag = Tag_D;\
            break;\
    default:\
            tag = Tag_B;\
}

enum Tag
{
  Tag_A,
  Tag_B,
  Tag_C,
  Tag_D
};

typedef enum Tag Tag;


void Lex_Char(char* chPtr, Tag* tag)
{
switch(*chPtr) { 
    case 'a':
    case 'b':
    case 'c':
    case 'e':
    case '$': *tag = Tag_A;
              break; 
    case '0':
    case '1':
    case '2':
    case '3': *tag = Tag_B;
             break;
    case 'r':
          if(chPtr[1] == 'd' || chPtr[1] == '@') *tag = Tag_C;
          else *tag = Tag_B;
          break;
    case '+':
    case '#':
    case '!':
         if(chPtr[1] == 'd') *tag = Tag_C;
    case '-':
    case '^':
            *tag = Tag_D;
            break;
    default:
            *tag = Tag_B;
     }  
}




int main(){
    Tag tagPnt = Tag_D;
    char* code = "#he";

    clock_t start, end;


    start = clock();

    //for(size_t i = 0; i<10000;i++) Lex_Char(code, &tagPnt); Number of seconds: 0.000067
    for(size_t i = 0; i<10000;i++) LEX_CHAR(code, tagPnt); // Number of seconds: 0.000032

    end = clock();
    printf( "Number of seconds: %f\n", (end-start)/(double)CLOCKS_PER_SEC );
    printf("%d is tag\n", tagPnt);


    return 0;
}

<强>结果:

  • 功能:0.000067
  • 微距:0.000032