What Is Better: Multiple "If" Statements or One "If" with Multiple Conditions

What is better: multiple if statements or one if with multiple conditions?

One golden rule I follow is to "Avoid Nesting" as much as I can. But if it is at the cost of making my single if condition too complex, I don't mind nesting it out.

Besides you're using the short-circuit && operator. So if the boolean is false, it won't even try matching!

So,

if (boolean_condition && matcher.find(string)) {
...
}

is the way to go!

Is better if statements with multiple conditions or more else if statements?

Let's make a simple experiment !

Dummy data

data <- data.frame(numerator = sample(c(0:9, NA), 10000, replace = T),
denominator = sample(c(0:9, NA), 10000, replace = T))

Two functions made up of two " if " conditions

f1 <- function(x){
num <- x[1] ; denom <- x[2]
if (is.na(num)){
result = 0
} else if (num == 0){
result = 0
} else if (is.na(denom)){
result = Inf
} else if (denom == 0){
result = Inf
} else {
result = num / denom
}
return(result)
}

f2 <- function(x){
num <- x[1] ; denom <- x[2]
if (is.na(num) || num == 0){
result = 0
} else if (is.na(denom) || denom == 0){
result = Inf
} else {
result = num / denom
}
return(result)
}

Benchmark analysis

library(microbenchmark)
library(ggplot2)

res <- microbenchmark(
type1 = {
quotient1 <- apply(data, 1, f1)
}, type2 = {
quotient2 <- apply(data, 1, f2)
}, times = 100
)

res
# Unit: milliseconds
# expr min lq mean median uq max
# type1 21.91925 23.70445 27.16314 25.52339 26.90110 122.91710
# type2 22.00139 23.64297 26.11080 25.04576 26.46136 42.62506

autoplot(res)

Sample Image

Conclusion

You can try the benchmark several times and you can find that
there is no significant difference between two if conditions.

If Else VS Multiple If Statements

Using only if, interpreter or whatever will test all conditions but if you use else if (if possible) then on passing any condition , next else if will not be tested or checked.

if (age < 10){
// do something
}
if (age < 20 && age > 10){
// do something
}
if (age < 30 && age > 20){
// do something
}

All conditions will be tested/compared

but in this scenario

if (age < 10){
// do something
}
else if (age < 20 && age > 10){
// do something
}
else if (age < 30 && age > 20){
// do something
}

if age is 5, only first condition will be tested.

If Statement with Multiple Conditions vs Individual If Statements

They are equivalent. The || operator short-circuits, so as soon as it reaches a condition that is true, it stops checking other conditions and returns true.

The | operator does not short-circuit, so it would evaluate every operand before returning a value.

Same goes for && and & but with false. As soon as a false condition is reached, the && operator returns false.

So your two methods are functionally identical. Use whichever one you think is the cleanest, easiest to infer intent, etc.

Or does the compiler optimize the code to work most efficient no matter how the source is written?

Well, the optimizer can make changes to code for efficiency, but it cannot change the functional behavior, meaning it won't change the order of the operations. Imagine you had something like:

if ((x == 0) || (y/x > 1))

this is a common way to prevent divide-by-zero errors. If the compiler could rearrange the operands, then it could introduce divide-by-zero errors.

Is there any difference between using multiple if statements and else if statements?

Yes, potentially. Consider this (C#, Java, whatever):

int x = GetValueFromSomewhere();

if (x == 0)
{
// Something
x = 1;
}
else if (x == 1)
{
// Something else...
}

vs this:

int x = GetValueFromSomewhere();

if (x == 0)
{
// Something
x = 1;
}
if (x == 1)
{
// Something else...
}

In the first case, only one of "Something" or "Something else..." will occur. In the second case, the side-effects of the first block make the condition in the second block true.

Then for another example, the conditions may not be mutually exclusive to start with:

int x = ...;

if (x < 10)
{
...
}
else if (x < 100)
{
...
}
else if (x < 1000)
{
...
}

If you get rid of the "else" here, then as soon as one condition has matched, the rest will too.

Are multiple 'if' statements and 'if-else-if' statements the same for mutually exclusive conditions?

When you write multiple if statements, it's possible that more than one of them will be evaluated to true, since the statements are independent of each other.

When you write a single if else-if else-if ... else statement, only one condition can be evaluated to true (once the first condition that evaluates to true is found, the next else-if conditions are skipped).

You can make multiple if statements behave like a single if else-if .. else statement if each of the condition blocks breaks out of the block that contains the if statements (for example, by returning from the method or breaking from a loop).

For example :

public void foo (int x)
{
if (x>7) {
...
return;
}
if (x>5) {
...
return;
}
}

Will have the same behavior as :

public void foo (int x)
{
if (x>7) {
...
}
else if (x>5) {
...
}
}

But without the return statements it will have different behavior when x>5 and x>7 are both true.

nested if vs. and condition

What happened when you tried it, and why would you expect there to be any difference at all with what the compiler produces? There is no difference in your algorithm between the two.

unsigned int one ( unsigned int myConditionA, unsigned int myConditionB, unsigned int myConditionC )
{
if(myConditionA){
if (myConditionB){
if (myConditionC){
//do something
return(1);
}
}
}
return(0);
}
unsigned int two ( unsigned int myConditionA, unsigned int myConditionB, unsigned int myConditionC )
{
if(myConditionA && myConditionB && myConditionC){
//do something
return(1);
}
return(0);
}

00000000 <one>:
0: e3510000 cmp r1, #0
4: 13520000 cmpne r2, #0
8: 13a02001 movne r2, #1
c: 03a02000 moveq r2, #0
10: e3500000 cmp r0, #0
14: 03a00000 moveq r0, #0
18: 12020001 andne r0, r2, #1
1c: e12fff1e bx lr

00000020 <two>:
20: e3510000 cmp r1, #0
24: 13520000 cmpne r2, #0
28: 13a02001 movne r2, #1
2c: 03a02000 moveq r2, #0
30: e3500000 cmp r0, #0
34: 03a00000 moveq r0, #0
38: 12020001 andne r0, r2, #1
3c: e12fff1e bx lr

hah, okay this was ugly but only because of how I wrote the test, had these been one or the other, but not both then the compiler would have produced the same code clearly. So doing a measured performance test on this might show a difference if you were able to see that, but it is a bad test.

00000000 <two>:
0: 10800006 beqz $4,1c <two+0x1c>
4: 00801025 move $2,$4
8: 10a00003 beqz $5,18 <two+0x18>
c: 00000000 nop
10: 03e00008 jr $31
14: 0006102b sltu $2,$0,$6
18: 00001025 move $2,$0
1c: 03e00008 jr $31
20: 00000000 nop

00000024 <one>:
24: 08000000 j 0 <two>
28: 00000000 nop

another instruction set.

00000000 <_one>:
0: 1d80 0002 mov 2(sp), r0
4: 0308 beq 16 <_one+0x16>
6: 0bf6 0004 tst 4(sp)
a: 0306 beq 18 <_one+0x18>
c: 15c0 0001 mov $1, r0
10: 0bf6 0006 tst 6(sp)
14: 0301 beq 18 <_one+0x18>
16: 0087 rts pc
18: 0a00 clr r0
1a: 0087 rts pc

0000001c <_two>:
1c: 1d80 0002 mov 2(sp), r0
20: 0308 beq 32 <_two+0x16>
22: 0bf6 0004 tst 4(sp)
26: 0306 beq 34 <_two+0x18>
28: 15c0 0001 mov $1, r0
2c: 0bf6 0006 tst 6(sp)
30: 0301 beq 34 <_two+0x18>
32: 0087 rts pc
34: 0a00 clr r0
36: 0087 rts pc

different compiler

00000000 <one>:
0: e3510000 cmp r1, #0
4: 13a01001 movne r1, #1
8: e3500000 cmp r0, #0
c: 13a00001 movne r0, #1
10: e3520000 cmp r2, #0
14: e0000001 and r0, r0, r1
18: 13a02001 movne r2, #1
1c: e0000002 and r0, r0, r2
20: e12fff1e bx lr

00000024 <two>:
24: e3510000 cmp r1, #0
28: 13a01001 movne r1, #1
2c: e3500000 cmp r0, #0
30: 13a00001 movne r0, #1
34: e3520000 cmp r2, #0
38: e0000001 and r0, r0, r1
3c: 13a02001 movne r2, #1
40: e0000002 and r0, r0, r2
44: e12fff1e bx lr

another target with the other compiler

00000000 <one>:
0: 27bdfff8 addiu $29,$29,-8
4: afbe0004 sw $30,4($29)
8: 03a0f025 move $30,$29
c: 0005082b sltu $1,$0,$5
10: 0004102b sltu $2,$0,$4
14: 00410824 and $1,$2,$1
18: 0006102b sltu $2,$0,$6
1c: 00221024 and $2,$1,$2
20: 03c0e825 move $29,$30
24: 8fbe0004 lw $30,4($29)
28: 03e00008 jr $31
2c: 27bd0008 addiu $29,$29,8

00000030 <two>:
30: 27bdfff8 addiu $29,$29,-8
34: afbe0004 sw $30,4($29)
38: 03a0f025 move $30,$29
3c: 0005082b sltu $1,$0,$5
40: 0004102b sltu $2,$0,$4
44: 00410824 and $1,$2,$1
48: 0006102b sltu $2,$0,$6
4c: 00221024 and $2,$1,$2
50: 03c0e825 move $29,$30
54: 8fbe0004 lw $30,4($29)
58: 03e00008 jr $31
5c: 27bd0008 addiu $29,$29,8

Even unoptimized I got the same result. There is no difference between the two so why would the compiler generate a difference such that might be a performance difference?



Related Topics



Leave a reply



Submit