简体   繁体   English

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

[英]Are macros for large switch statements faster than functions with large switch statements?

So, for inline functions (1-2 statements) and small macros, it seems that there isn't too much of a performance difference between using a macro or an inline function. 因此,对于内联函数(1-2条语句)和小宏,似乎在使用宏或内联函数之间没有太大的性能差异。

However, given the function call overhead for a larger function, I am wondering for, 但是,鉴于较大函数的函数调用开销,我想知道,

  • GCC 7.0 GCC 7.0
  • C language (not C++) C语言(不是C ++)
  • OSX (not sure if this would vary that much cross platform) OSX(不确定跨平台的差异是否很大)

Would using large macros for a switch statement be faster than putting them in an equivalent function call? 将大型宏用于switch语句是否比将它们放入等效函数调用中更快? This is on my part assuming such large functions will not be inlined. 在我这方面,假设不会内联这么大的功能。 Here is my example code. 这是我的示例代码。

#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;
     }  
}

So between these two, the macro and the function, is there any optimization in using the macro over the function? 那么在宏和函数这两者之间,在函数上使用宏是否有任何优化?

First, note that when you have code in a macro, the compiler must insert it inline in the calling code. 首先,请注意,当宏中包含代码时,编译器必须将其内联插入到调用代码中。 When you make it a function, the compiler may insert it inline. 当您将其设为函数时,编译器可能会内联插入它。

You should also understand what happens when you declare a function like: 您还应该了解在声明如下函数时会发生什么:

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

This tells the compiler that the function is accessible from other C files - the compiler has to make a full version of this function. 这告诉编译器可以从其他C文件访问该功能-编译器必须制作此功能的完整版本。 Inlining the function would then mean making two copies of the code - one for the full function version, and one inlined at the call site. 内联函数将意味着制作两个代码副本-一个用于完整功能版本,另一个在调用站点内联。 The compiler will be reluctant to do this unless your optimisation settings put a strong emphasis on size over speed. 除非您的优化设置非常强调大小而不是速度,否则编译器将不愿这样做。

If a function is only to be used within the current translation unit, you should mark it "static": 如果仅在当前翻译单元中使用功能,则应将其标记为“静态”:

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

This tells the compiler that it is inaccessible from outside. 这告诉编译器从外部无法访问它。 If the function is only ever used once within the current module, then the compiler can happily inline it - doing so is "free". 如果该函数在当前模块中仅使用过一次,则编译器可以愉快地内联它-这样做是“免费的”。

You can also mark the function as "static inline", giving the compiler a hint that you are keen on it being inlined. 您还可以将函数标记为“静态内联”,从而向编译器提示您希望将其内联。

Of course, this all depends on having optimisation enabled for the compiler - if you don't enable optimisation, all your time testing is worthless. 当然,这全部取决于为编译器启用优化-如果您不启用优化,则所有时间测试都是毫无用处的。

An inlined static function is always a better choice than a macro (when you have the choice - macros can be more flexible than inline functions). 内联静态函数总是比宏更好的选择(当您可以选择时-宏比内联函数更灵活)。 The code is clearer to write, and you have better static warning and error checking. 该代码更容易编写,并且具有更好的静态警告和错误检查。 The resulting code (assuming optimisation) will be the same. 结果代码(假设优化)将相同。

Your timing tests, incidentally, are pointless here - the compiler will see that the values involved don't change and will not run the function more than once, when it is inlined and optimisation is enabled. 顺便说一下,您的时序测试在这里没有意义-内联并且启用优化后,编译器将看到所涉及的值不会更改,并且不会多次运行该函数。 It might not run it at all, but pre-calculate the result at compile time. 它可能根本不运行它,但是会在编译时预先计算结果。

Oh, and you've forgotten a "break" in the case '!'. 哦,您忘记了“!”情况下的“中断”。

So it turns out after a timed test, and repeated under an identical for loop, the macro version is about twice as fast as the regular function. 因此,经过定时测试后发现,并在相同的for循环下重复执行,宏版本的速度大约是常规函数的两倍。

Here's is my complete timer and full file compiled to produce the result 这是我完整的计时器和完整的文件,可以生成结果

#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;
}

Result: 结果:

  • Function: 0.000067 功能:0.000067
  • Macro: 0.000032 宏:0.000032

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM