http://www.perlmonks.org?node_id=301436


in reply to Re: Re: Re: Re: Re: Re: Perl Idioms Explained: && and || "Short Circuit" operators
in thread Perl Idioms Explained - && and || "Short Circuit" operators

Modern C compilers have the smarts built in to make intelligent decisions on what code to emit. In the case of your example of 2 widely spaced cases, it produces this

?live1@0: ; ; int main( int argc, char * argv[] ) { ; push ebp mov ebp,esp push ecx ; ; unsigned long selector; ; sscanf( argv[1], "%li", &selector ); ; @1: lea eax,dword ptr [ebp-4] push eax push offset s@ mov edx,dword ptr [ebp+12] push dword ptr [edx+4] call _sscanf add esp,12 ; ; ; switch ( selector ) { ; mov ecx,dword ptr [ebp-4] sub ecx,-2147483648 je short @5 sub ecx,-2147483647 jne short @2 ; ; case 1 : printf( "case 1\n" ); +break; ; push offset s@+4 call _printf pop ecx @8: pop ecx pop ebp ret ; ; case 2147483648 : printf( "case 2147483648\n" ); + break; ; @5: push offset s@+12 call _printf pop ecx @9: pop ecx pop ebp ret ; ; default : printf( "default\n" ); + break; ; @2: push offset s@+29 call _printf pop ecx ; ; } ; } ; @6: @7: pop ecx pop ebp ret _main endp _TEXT ends

Which as you can see, gets translated into a test and jump for the extreme case, another test and jump for the default case and a fall through for the 1 case.

If you combine a contiguous range and an extreme case, you get.

_TEXT segment dword public use32 'CODE' _main proc near ?live1@0: ; ; int main( int argc, char * argv[] ) { ; push ebp mov ebp,esp push ecx ; ; unsigned long selector; ; sscanf( argv[1], "%li", &selector ); ; @1: lea eax,dword ptr [ebp-4] push eax push offset s@ mov edx,dword ptr [ebp+12] push dword ptr [edx+4] call _sscanf add esp,12 ; ; ; switch ( selector ) { ; mov ecx,dword ptr [ebp-4] cmp ecx,3 jg short @11 je short @7 sub ecx,-2147483648 je short @10 sub ecx,-2147483647 je short @9 dec ecx je short @8 jmp short @2 @11: sub ecx,4 je short @6 dec ecx je short @5 dec ecx je short @4 jmp short @2 ; ; case 1 : printf( "case 1\n" ); +break; ; @9: push offset s@+4 call _printf pop ecx @14: pop ecx pop ebp ret ; ; case 2 : printf( "case 2\n" ); + break; ; @8: push offset s@+12 call _printf pop ecx @15: pop ecx pop ebp ret ; ; case 3 : printf( "case 3\n" ); + break; ; @7: push offset s@+20 call _printf pop ecx @16: pop ecx pop ebp ret ; ; case 4 : printf( "case 4\n" ); + break; ; @6: push offset s@+28 call _printf pop ecx @17: pop ecx pop ebp ret ; ; case 5 : printf( "case 5\n" ); + break; ; @5: push offset s@+36 call _printf pop ecx @18: pop ecx pop ebp ret ; ; case 6 : printf( "case 6\n" ); + break; ; @4: push offset s@+44 call _printf pop ecx @19: pop ecx pop ebp ret ; ; case 2147483648 : printf( "case 2147483648\n" ); + break; ; @10: push offset s@+52 call _printf pop ecx @20: pop ecx pop ebp ret ; ; default : printf( "default\n" ); + break; ; @2: push offset s@+69 call _printf pop ecx ; ; } ; } ; @12: @13: pop ecx pop ebp ret _main endp _TEXT ends

Which has been converted into a test cascade something like the original Perl code. However, this is before the optimiser has gone to work on it. Once the optimiser sees that code, it is quite likely that it will revert to using a dispatch table for the continuous cases with a single test for the extreme case. I having having trouble obtaining a post - optimisation disassembly that is readable. I'll try to update with that later.

As you might guess, if your range of cases is a widely spread set of discontiguous cases, then the result is you end up with cascaded tests just like the perl version. However, in almost every case (sic:) I remember using, I found some fairly simple function theat mapped the range to a contiguous or near-contiguous integer range. When this isn't the case, then I use an if/else cascade instead.


Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
Hooray!