John Smith
John Smith

Reputation: 897

Does evaluating 'else if' take as much CPU time as evaluating 'if' in C?

Initial assumption: We will run the following programs 1000 times with argv[1] not being NULL :

#include<stdio.h>
#include<stdlib.h>


int main( int argc, char *argv[] )  {
 
 if ( argv[1] == NULL ) {
  printf("Usage: a.out argument1");
  exit(1);
}
 else if ( argv[1] != NULL ) {
    
    some_cheap_computation;
    
   
   }

return 0;   
}

Will the above code run faster in such case than the following:

#include<stdio.h>
#include<stdlib.h>


int main( int argc, char *argv[] )  {
 
 if ( argv[1] == NULL ) {
  printf("Usage: a.out argument1");
  exit(1);
}
 if ( argv[1] != NULL ) {
    
    some_cheap_computation;
    
   
   }

return 0;   
}

I've already learned that the fastest code would make use of else instead of else if. Does evaluating else if take as much CPU time as evaluating if in C?

Upvotes: 0

Views: 169

Answers (4)

Richard Kirk
Richard Kirk

Reputation: 331

Previous posts have emphasised how your answer will depend on the compiler, and the architecture. Amen to all that. Now, here's a slightly different sort of answer.

Have you ever tried to write an optimiser? I wrote one for an interpreter, not a compiler. It is the devil of a job: you always think of extra cases you can optimise, but each case has to be checked so it actually performs in all the cases. So, consider the following bit of code...

if (x == 0) n = 1;
else if (x == -x) n = 2;
else n = 3;

If you are writing a whole sequence of else if tests, you might want to arrange the tests so the most likely test finishes first. If your input had a lot more zero numbers than non-zero ones then it may help to put the test for zero at the top, so most cases are dealt with as quickly as possible. You might want to make the more complicated test only happen if the simple one fails to save processing. So the best thing an optimiser can often do is to assume the person who wrote the code should know best.

But the test on the else if line is only true if x == 0. You should never get n == 2. A perfect optimiser ought to ignore this test. But if you have a long string of else if tests it will need to test that each statement is possible given any combination of any of the previous tests. This is hard to do, and not very rewarding in the general case. So optimisers may perform miracles in other parts of the code, but leave tangles of conditionals alone.

Upvotes: 0

Tzig
Tzig

Reputation: 851

In theory, we have no idea (and we probably shouldn't care): the C reference documentation only very rarely talks about things like number of cycles for statements and for a good reason, it's very architecture dependent.

On X86

To answer your question, let's test it with some good old A/B testing. I'm not good at writing assembly though and the x86_64 instruction set is way too complicated for me to even try so I asked gcc to compile this program with gcc -O3 -S for me:

int main(int argc, char** argv)
{
    int ret = 0;

    if(argc > 1)
    {
        ret += argc + 1;
    }
    else if(argc > 2)
    {
        ret += argc +2;
    }

    return ret;
}

and the assembly listing it created is:

main:
    xorl    %edx, %edx
    leal    1(%rdi), %eax
    cmpl    $1, %edi
    cmovle  %edx, %eax
    ret

I then removed the else in the else if statement line 12 and asked gcc to recompile the program:

main:
    xorl    %eax, %eax
    cmpl    $1, %edi
    jle .L1
    leal    1(%rdi), %eax
    cmpl    $2, %edi
    je  .L1
    leal    2(%rax,%rdi), %eax
.L1:
    ret

As you can see, there's some difference between the two listings with the else if version being 3 instructions and 1 label shorter. How long would those instructions take? We have no idea, and I mean it. Modern architectures are so complex with things such as pipelines, multiple levels of cache that may need to be cleared, SMTs... Maybe it will take longer than the "if" version on your new AMD Ryzen 9 and it would be the other way around on your old Intel Core 2 Duo, from your perspective as a developer there's virtually no way to know.

On other architectures

Well, you can test if you want to though but... Do you? From what you just read you probably understand it's not as easy as just saying

"if else" is faster than "if"

as if it was an universal truth. It depends on so many factors that you really shouldn't care about it. Maybe there's an architecture out there where "if" is 10 times faster than "else if" and you'll actually have a slower program on this architecture, maybe on some very parallelized CPU both will be executed and the result is discarded at the end, taking the exact same time in both cases...

The only industry I know of that actually did CPU optimization on a large scale is the gaming industry: every single PlayStation 2 has the same CPU whether it's the first one ever made or it was bought 5 years later. For game programmers at the time it made sense to optimize for one specific CPU as you had to make your game fit on it. Nowadays though you get things like the PS4 with a "Pro" model having a different CPU so even game studios stopped optimizing so much.

Does it even matter?

I don't think so. As commenters have pointed out under your question you'll probably want to optimize your code and algos first as just a single unneeded assignation could cost you more than those 3 instructions.

Another factor is how your compiler handles your code: modern x86 compilers are very good at understanding your code and will remove as many instructions as possible: you may think my test program above is kinda over-complicated just to test for a 4 characters difference but when I tried to make my program even a tiny bit simpler gcc didn't use conditional jumps at all, it saw a pattern I didn't and made the program go from ~10 instructions to just 5.

So... What should I do then?

If you want to optimize for a very specific CPU and every single nanosecond matters, do loads of A/B testing about what statements are faster. If you're just a normal programmer trying to make a program work while being as fast as it can try to make your intent clear to the compiler, use "else if" if that's what you want your program to do and use "if" if it makes more sense for your program.

This advice works for any other "what is this fastest between those two methods" by the way, use a switch case if it makes more sense, use ++var where is makes sense for your program, the compiler will do its thing anyway

Upvotes: 2

bitmask
bitmask

Reputation: 34664

It literally makes no difference. The compiler will optimise all versions to the same code. It can see that the second condition is the opposite of the first. This holds true for at least -O1. With -O0 there is zero point in arguing over performance.

See Compiled version 1.

See Compiled version 2.

Both generate this assembly:

.LC0:
        .string "Usage: a.out argument1"
main:
        mov     rax, QWORD PTR [rsi+8]
        test    rax, rax
        je      .L6
        movsx   eax, BYTE PTR [rax]
        add     eax, 1
        ret
.L6:
        sub     rsp, 8
        mov     edi, OFFSET FLAT:.LC0
        call    printf
        mov     edi, 1
        call    exit

Upvotes: 5

Dominique
Dominique

Reputation: 17565

I've made a small simulation to clear out the difference between subsequent ìf and usage of else if:

Case 1:

     if (a==1) { }
else if (a==2) { }
else if (a==3) { }
else if (a==4) { }
else if (a==5) { }
else if (a==6) { }
else if (a==7) { }
else if (a==8) { }
else if (a==9) { }
else if (a==10) { }

Case 2:

if (a==1) { }
if (a==2) { }
if (a==3) { }
if (a==4) { }
if (a==5) { }
if (a==6) { }
if (a==7) { }
if (a==8) { }
if (a==9) { }
if (a==10) { }

Different situations (a being 1, 5, 10):

Amount of checks which are performed:

     a|   1|   5|  10
======+====+====+====
Case 1|   1|   5|  10
Case 2|  10|  10|  10

So, else if is preferable (in order to limit the amount of checks).

Upvotes: 1

Related Questions