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:

If you want to make your code clear, then it's perhaps better to use a switch statement. But don't forget to add comments to each case about which situation it represents.

If clarity is not an issue, use "Single-check" if-else if tree.

Thanks for reading, code safely!
 


Similar Articles