Reputation: 10144
(C) If I have a function that contains an if which, if the condition is true can then return a certain value an then else return a different value. Is it more or less efficient to use an else or not bother?
i.e. ...
int foo (int a) {
if ((a > 0) && (a < SOME_LIMIT)) {
b = a //maybe b is some global or something
return 0;
} else {
return 1;
}
return 0;
}
or just
int foo (int a) {
if ((a > 0) && (a < SOME_LIMIT)) {
b = a //maybe b is some global or something
return 0;
}
return 1;
}
Assume GCC, will the first implementation result in compiled code being any different from the second one?
I need to be as efficient as possible here, so possible reduction of a branch for the else would be nice - but stylistically my inner OCD doesn't like to see a return that isnt 0 or void as the last instruction in a function as its less clear what is going on. So if it will be got rid of anyway then I could leave the else there...
Upvotes: 3
Views: 21201
Reputation: 44250
int foo (int a) {
/* Nothing to do: get out of here */
if (a <= 0 || a >= SOME_LIMIT) return 1;
b = a; // maybe b is some global or something
return 0;
}
There are barely any differences with respect to eficiency (the costliest part is the function call plus the return anyway).
For human readers, the least "indented" code (such as the above) is the easiest to read and understand.
BTW
The generated assembler also looks pretty minimal to me, and completely equivalent to the if (a >0 && a < LIMIT)
form.
.file "return.c"
.text
.p2align 4,,15
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
leal -1(%rdi), %edx
movl $1, %eax
cmpl $2998, %edx
ja .L2
movl %edi, b(%rip)
xorb %al, %al
.L2:
rep
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.globl b
.bss
.align 16
.type b, @object
.size b, 4
b:
.zero 4
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
.section .note.GNU-stack,"",@progbits
Upvotes: 1
Reputation: 2216
You can run gcc with the -O3 -S
options to generate optimized assembly code, so you can see (and compare) the optimized assembly. I made the following changes to your sources to make them compile.
File a.c:
int b;
int foo (int a) {
if ((a > 0) && (a < 5000)) {
b = a;
return 0;
} else {
return 1;
}
return 0;
}
File b.c:
int b;
int foo (int a) {
if ((a > 0) && (a < 5000)) {
b = a;
return 0;
}
return 1;
}
When compiling a.c with gcc -O3 -S a.c
the file a.s is created. On my machine it looks like this:
.file "a.c"
.text
.p2align 4,,15
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
movl 4(%esp), %edx
movl $1, %eax
leal -1(%edx), %ecx
cmpl $4998, %ecx
ja .L2
movl %edx, b
xorb %al, %al
.L2:
rep
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.comm b,4,4
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
.section .note.GNU-stack,"",@progbits
When compiling b.c with gcc -O3 -S b.c
the file b.s is created. On my machine it looks like this:
.file "b.c"
.text
.p2align 4,,15
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
movl 4(%esp), %edx
movl $1, %eax
leal -1(%edx), %ecx
cmpl $4998, %ecx
ja .L2
movl %edx, b
xorb %al, %al
.L2:
rep
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.comm b,4,4
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
.section .note.GNU-stack,"",@progbits
Notice that the assembled implementations of foo:
are identical. So, in this case, with this version of GCC, it does not matter which way you write the code.
Upvotes: 9
Reputation: 7448
You won't get to the last return 0;
in the first example. I would say your second one is stylistically clearer at the very least because of that. Less code for the same thing is usually a good thing.
With regards to the performance, you can check out the assembly executable if you fancy such a thing or profile the code and see if there's an actual difference. My bet is none that matters.
Finally, if your compiler supports optimisation flags, use them!
Upvotes: 1
Reputation: 405
I would write it like this...
int foo (int a) {
if ((a > 0) && (a < SOME_LIMIT)) {
b = a //maybe b is some global or something
return 0;
} else {
return 1;
}
}
The whole function is just a boolean to condition the value of b to be bigger than zero and less than some constant. And the if statement conditions the return. There is no need to add a default return to the function. The default return will invalidate the if condition.
Upvotes: 1
Reputation: 9097
Of course it depends on the compiler. I guess every (decent) compiler on the market will produce exactly the same output in both cases. However trying such micro-optimizations is disencouraged by any book about optimization!
Choose the form which is better readable.
Upvotes: 1
Reputation: 2842
Check the object file between both implementations. Put an assembly header such as
PortionInQuestion:
That will then show up in your assembly file as a label and you can see how the assembly generated is different. They might not be different at all (because of the optimizations), or they could be completely different. Without seeing hte raw assembly, there's no way to tell how the compiler is optimizing it.
Upvotes: 1