Labels as Values ​​Compared to the Switch Statement

I recently read about methods as values,

int main(){ int value = 2; const void *labels[] = {&&val_0, &&val_1, &&val_2}; goto *labels[value]; val_0: printf("The value is 0\n"); goto end; val_1: printf("The value is 1\n"); goto end; val_2: printf("The value is 2\n"); goto end; end: return(0); } 

what I'm asking is that is there really any performance gain from using this method instead of using a switch statement or an array of pointers?

+4
source share
5 answers

This non-standard extension will most likely not be better than the equivalent switch expression in this case, and this, IMHO, should be avoided. The switch statement is more intuitive and easy to maintain.

(I quickly tested my version of gcc and issued the exact same code for both this code and the switch statement. This was not a representative test, although it optimized everything except for the selected code path.)

One potential performance concern is that the switch statement should have reasonable behavior, even if value not in the correct range, your version has undefined behavior, so the compiler can avoid checking the range in some code.

+5
source

I think the switch statement is more readable and clean, so use it and not an array if the problem is not very suitable for the switch statement.

The best use of a label as a value is used in the interpreter for a code stream:

Labels inside the interpreter function can be stored in multi-threaded code for superfast dispatching.

+3
source

However, efficient switch compilation is still an open problem; see, for example, this document .

And although the label as values ​​and indirect goto -s are indeed a GCC language extension, it was adopted by other compilers ( icc , LLVM / clang ) and is really useful (and is low-level, in the spirit of C), especially for coding code interpreters with thread code, automatic machines, etc. I don’t know why this extension is not becoming standard (I think that it is not standardized due to social or economic factors, not because of technical futility.).

+2
source

The answer lies in the assembly code generated by gcc ( -g and -O2 ). Two functions take value as an argument, which is first requested by the user (to force gcc not to delete unused code fragments, as well as the exception of dead code). Of course, the printf part is the same for both functions and gcc is optimized (for both), so it returns immediately after printing. Thus, a significant part is the beginning of both functions. Let them take a look:

  • goto function:
 0x080484d0 <+0>: push %ebp # - 0x080484d1 <+1>: mov %esp,%ebp # |- standard prologue 0x080484d3 <+3>: sub $0x28,%esp # - 0x080484d6 <+6>: mov 0x8(%ebp),%eax # get argument 0x080484d9 <+9>: movl $0x80484f8,-0x14(%ebp) # set up labels array 0x080484e0 <+16>: movl $0x8048510,-0x10(%ebp) 0x080484e7 <+23>: movl $0x8048528,-0xc(%ebp) 0x080484ee <+30>: jmp *-0x14(%ebp,%eax,4) # jump to appropriate sect. 0x080484f2 <+34>: lea 0x0(%esi),%esi 
  • Switch function:
 0x08048470 <+0>: push %ebp # - 0x08048471 <+1>: mov %esp,%ebp # |- standard prologue 0x08048473 <+3>: sub $0x18,%esp # - 0x08048476 <+6>: mov 0x8(%ebp),%eax # get argument 0x08048479 <+9>: cmp $0x1,%eax 0x0804847c <+12>: je 0x80484b8 <switchFunc+72> # jump here if value == 1 0x0804847e <+14>: cmp $0x2,%eax 0x08048481 <+17>: je 0x80484a0 <switchFunc+48> # if value == 2 0x08048483 <+19>: test %eax,%eax 0x08048485 <+21>: jne 0x804849b <switchFunc+43> # if value != 0 return 

Both fragments have a “slow part”: the first spends most of the time creating an array of labels, while the second slowly decides which path to choose. So basically, their runtime is almost the same.
Which one is better? The second, one with a switch construct. It is standard C, more readable, convenient, and understandable.

+2
source

Here is the documentation for this feature from the gnu gcc manual http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html

0
source

Source: https://habr.com/ru/post/1379770/


All Articles