# Performance of If-else if tree vs. Switch (multiple variables) in C#

Recently I was writing a program with 3 binary values and I had to handle all 8 possible "situations" and switch is said to be faster, because it uses an indexed branch table. However, it accepts only one value, while I had 3 different values to check.

I've come up with a little trick to store all 8 possible situations into one single integer value:

switch ((a == 1 ? 1 : 0) + (b == 1 ? 2 : 0) + (c == 1 ? 4 : 0))
{

case 0: break; //none true
case 1: break;
//a true
case 2: break;
//b true
case 3: break;
//a and b true
case 4: break;
//c true
case 5: break;
//a and c true
case 6: break;
//b and c true
case 7: break;
//all true
}

The mathematical formula for each variable:

• n: numeral system base [2, ∞]
• m: zero-based index of the checked variable [0, ∞]
• v: zero-based index of the variable's value [0, n - 1]

result for one variable = nm * v

As an example, 3 ternary variables:

int situation = (a == 0 ? 0 : (a == 1 ? 1 : 2)) + (b == 0 ? 0 : (b == 1 ? 3 : 6)) + (c == 0 ? 0 : (c == 1 ? 9 : 18));

The switch above has to (in every case) get-and-check 4 times (3 times in the calculation, 1 time to match a case). However, it also has to perform the addition.

So, is switch in this case faster? I have tested switch's performance (every single situation 100,000,000 times) against the following if-else if trees:

1. The obvious one:

if (a == 1 && b == 1 && c == 1) ;
else if (a == 1 && b == 1) ;
else if (a == 1 && c == 1) ;
else if (b == 1 && c == 1) ;
else if (a == 1) ;
else if (b == 1) ;
else if (c == 1) ;
else ;

 Condition Num of checks Check sequence a & b & c 3 a,b,c a & b 5 a,b,c,a,b a & c 6 a,b,a,b,a,c b & c 5 a,a,a,b,c a 8 a,b,a,b,a,c,a b 8 a,a,a,b,c,a,b c 8 a,a,a,b,a,b,c none 8 a,a,a,b,a,b,

Average: 6.375 checks

Switch/if-tree ratio:

• calculation into a variable first: 9/10
• calculation straight into switch: 3/4

Switch is faster.

My testing code:

int
counter = 0;
Timer
timer = new Timer(1);
timer.Elapsed += (object o, ElapsedEventArgs e) =>
{
++counter;
};

timer.Start();
for
(int i = 0; i < 100000000; ++i)
{
for (int a = 0; a < 2; ++a)
{
for (int b = 0; b < 2; ++b)
{
for (int c = 0; c < 2; ++c)
{
if (a == 1 && b == 1 && c == 1) ;
else if (a == 1 && b == 1) ;
else if (a == 1 && c == 1) ;
else if (b == 1 && c == 1) ;
else if (a == 1) ;
else if (b == 1) ;
else if (c == 1) ;
else ;
}
}
}
}
timer.Stop();

Console
.WriteLine(counter.ToString());

counter = 0;
timer.Start();

for
(int i = 0; i < 100000000; ++i)
{
for (int a = 0; a < 2; ++a)
{
for (int b = 0; b < 2; ++b)
{
for (int c = 0; c < 2; ++c)
{
switch ((a == 1 ? 1 : 0) +
(b == 1 ? 2 : 0) +
(c == 1 ? 4 : 0))
{
case 0: break;
case 1: break;
case 2: break;
case 3: break;
case 4: break;
case 5: break;
case 6: break;
case 7: break;
}
}
}
}
}
timer.Stop();

Console
.WriteLine(counter.ToString());

2. Dual-check/single-check:

if (a && b)
{
if (c) ;
else ;
}
else if (a && c) ;
else if (b && c) ;
else if (a) ;
else if (b) ;
else if (c) ;
else ;

 Condition Num of checks Check sequence a & b & c 3 a,b,c a & b 3 a,b,c, a & c 4 a,b,a,c b & c 5 a,a,a,b,c a 6 a,b,a,c,b,a b 6 a,a,b,c,a,b c 6 a,a,b,a,b,c none 6 a,a,b,a,b,c

Average: 4.875 checks

Switch/if-tree ratio:

• calculation into a variable first: 18/17
• calculation straight into switch: 1/1

Same speed unless you use a separate variable.

3. Single-check:

if (a)
{
if (b)
{
if (c) ;
else ;
}
else if (c) ;
else ;
}
else if (b)
{
if (c) ;
else ;
}
else if (c) ;
else ;

 Condition Num of checks Check sequence a 3 a,b,c a & b 3 a,b,c, a & b & c 3 a,b,c a & c 3 a,b,c b 3 a,b,c b & c 3 a,b,c c 3 a,b,c none 3 a,b,c

Average: 3 checks

Switch/if-tree ratio:

• calculation into a variable first: 14/9
• calculation straight into switch: 3/2

If-else if tree is quite faster. This is expected, because it makes 3 checks, while switch makes 4.

4. What if we make the switch nested too?

switch (a)
{
case 0:
switch (b)
{
case 0:
switch (c)
{
case 0: break;
// none
case 1: break;
// c
}
break;
case 1:
switch (c)
{

case 0: break; // b
case 1: break;
// b & c
}
break;
}
break;
case 1:
switch (b)
{
case 0:
switch (c)
{
case 0: break;
// a
case 1: break;
// a & c
}
break;
case 1:
switch (c)
{
case 0: break;
// a & b
case 1: break;
// a & b & c
}
break;
}
break;
}

Switch/if-tree ratio: 15/14

If-else if tree still slightly faster.

All these ratios were very consistent, average relative mistake was 0.6%.

Conclusion: