9

I was creating a C# method in visual studio that contained only a switch statement where each case returned a value. By personal habit, I put something similar to the following code:

private string SwitchMethod(int num)
    {
        switch (num)
        {
            case 0:
                return "result 1";
            case 1:
                return "result 2";
            case 2:
                return "result 3";
        }
        return "no result";
    }

My question is this: Which code will have better performance? The code above or below, or are the same? And why?

I would assume that because of compiler optimizations...they might just be the same...but I really don't know.

private string SwitchMethod(int num)
    {
        switch (num)
        {
            case 0:
                return "result 1";
            case 1:
                return "result 2";
            case 2:
                return "result 3";
            default:
                return "no result";
        }
    }

REVISION: It seems that I should be more specific: When compiled...will less efficient code be generated by one or the other?

I realize that the difference in performance may be insignificant...I'm just curious really.

bsara
  • 7,940
  • 3
  • 29
  • 47
  • 4
    This is pretty subjective. I avoid having multiple exit points from a method or function, but YMMV. If you're really curious, have a look at the IL. – 3Dave Feb 27 '12 at 22:32
  • 1
    That compiled? I'm not in front of my compiler, but I'm almost certain a default is required. – Eric J. Feb 27 '12 at 22:33
  • Throw an exception saying "shouldn't get here" for default if you really think it won't happen – Matt Feb 27 '12 at 22:35
  • Either a `default` or an extra return after the `switch()` is required. – H H Feb 27 '12 at 22:35
  • 2
    OK, now you have fixed your code. And you have nicely written the code both ways. How did each perform when you ran them? – Anthony Pegram Feb 27 '12 at 22:35
  • How is this either efficient or inefficient? As it stands it won't compile because its possible to fall out of the switch statement and therefore there would be no return value. However that isn't (I assume) the point you're making. I've been advised the opposite (always include the default case) if nothing more than to ensure that I consider how the default case should be answered – kaj Feb 27 '12 at 22:35
  • What's the actual question? The one from the title is rather vague. – H H Feb 27 '12 at 22:35
  • Sorry, I accidently hit tab when writing the question and not realizing that the "Post Comment" button had the focus and I hit enter. – bsara Feb 27 '12 at 22:36
  • First not all paths return value. second try to call this function in loop for 10000 times with and without the default value and check if their is defferent – Mohammad Shraim Feb 27 '12 at 22:36
  • @EricJ. Default is not required, though recommended – Oskar Kjellin Feb 27 '12 at 22:39
  • Someone correct me if I'm wrong: Is it really worth Micro-optimizing this much? Trials you run are likely to perform differently on different architectures depending on how this compiles to into native code by the JIT Compiler on different architectures and the hardware used. Low-level things like branch prediction might come into play. – Joshua Enfield Feb 27 '12 at 22:41
  • @JoshuaEnfield Default or not is a matter of taste, not performance. Switch can be a lot faster than ifs though – Oskar Kjellin Feb 27 '12 at 23:04

3 Answers3

11
public static string foo(int num)
        {
            switch (num)
            {
                case 0:
                    return "result 1";
                case 1:
                    return "result 2";
                case 2:
                    return "result 3";
            }
            return "no result";
        }

Becomes:

.method public hidebysig static string  foo(int32 num) cil managed
{
  // Code size       57 (0x39)
  .maxstack  1
  .locals init ([0] string CS$1$0000,
           [1] int32 CS$4$0001)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  stloc.1
  IL_0003:  ldloc.1
  IL_0004:  switch     ( 
                        IL_0017,
                        IL_001f,
                        IL_0027)
  IL_0015:  br.s       IL_002f
  IL_0017:  ldstr      "result 1"
  IL_001c:  stloc.0
  IL_001d:  br.s       IL_0037
  IL_001f:  ldstr      "result 2"
  IL_0024:  stloc.0
  IL_0025:  br.s       IL_0037
  IL_0027:  ldstr      "result 3"
  IL_002c:  stloc.0
  IL_002d:  br.s       IL_0037
  IL_002f:  ldstr      "no result"
  IL_0034:  stloc.0
  IL_0035:  br.s       IL_0037
  IL_0037:  ldloc.0
  IL_0038:  ret
} // end of method Program::foo

Moving the return into a default case:

.method public hidebysig static string  foo(int32 num) cil managed
{
  // Code size       57 (0x39)
  .maxstack  1
  .locals init ([0] string CS$1$0000,
           [1] int32 CS$4$0001)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  stloc.1
  IL_0003:  ldloc.1
  IL_0004:  switch     ( 
                        IL_0017,
                        IL_001f,
                        IL_0027)
  IL_0015:  br.s       IL_002f
  IL_0017:  ldstr      "result 1"
  IL_001c:  stloc.0
  IL_001d:  br.s       IL_0037
  IL_001f:  ldstr      "result 2"
  IL_0024:  stloc.0
  IL_0025:  br.s       IL_0037
  IL_0027:  ldstr      "result 3"
  IL_002c:  stloc.0
  IL_002d:  br.s       IL_0037
  IL_002f:  ldstr      "result 4"
  IL_0034:  stloc.0
  IL_0035:  br.s       IL_0037
  IL_0037:  ldloc.0
  IL_0038:  ret
} // end of method Program::foo

Exactly the same. No performance difference. I changed the "no result" to result 4 just to make sure the code was regenerated. Apparently the C# compiler optimizes it or it just ends up being equivalent.

Joshua Enfield
  • 17,642
  • 10
  • 51
  • 98
  • Thanks so much! Forgive my ignorance...but what did you use to generate the assembly? – bsara Feb 27 '12 at 23:02
  • 3
    @Brandon There are plenty. For instance ilspy – Oskar Kjellin Feb 27 '12 at 23:03
  • 2
    *ILDasm* comes with the .NET Framework SDK. Keep in mind this is IL Assembly Code which is not what the CPU sees. You can use a tool that also comes with the SDK NGen.exe to generate that assembly, but it is likely to optimize it differently depending on the hardware. – Joshua Enfield Feb 27 '12 at 23:04
  • 1
    @OskarKjellin - Nice mention with ILSpy. Now that Reflector is $$$ it's cool to see a free tool. – Joshua Enfield Feb 27 '12 at 23:32
4

It is good practice to always include a default case at the base of a switch for situations where none of the other cases are valid.

It is not an efficiency problem because if one of the previous cases is hit - your program will not check any other cases (it is the equivalent of using if/else if/else - where the final else is the default).

Hope this helps.

DIXONJWDD
  • 1,276
  • 10
  • 20
  • Your answer is ok, but one should be careful in taking what you mean by equivalent: http://stackoverflow.com/questions/445067/if-vs-switch-speed – Joshua Enfield Feb 27 '12 at 22:38
  • But when compiled...will more code be generated by one or the other? Thus resulting in more instructions being executed when none of the expected conditions are met? – bsara Feb 27 '12 at 22:39
  • Sorry - yes of course switch statements are faster in terms of execution speed. I meant in terms of how they work, they are equivalent. Brandon - I would imagine that the assembly code that would underlie this would be extremely similar and that no extra instructions would be executed. It is likely that the compiler may actually make your code as efficient as possible on compilation and thus produce the exact same assembly code. – DIXONJWDD Feb 27 '12 at 22:47
  • The more code you write, the more IL is generated in Debug config so you could set break points, etc. for debugging. But in Release config, slow stuff is optimized away. – TimTIM Wong Jun 09 '22 at 17:08
3

Instead of a function and a switch statement you would be better off with a generic Dictionary where the key is KofaxEnvironment and the value is the same as what you're returning from the switch. Something like:

Dictionary<KofaxEnvironment, string>

or

Dictionary<int, string>

I also wouldn't worry about performance. Correctness should be your first goal.

But, if you do stick with a switch use a default that throws an exception:

default:
    throw new ArgumentException("Serious programmer error!");

And as for performance, the difference (if any at all) between a switch default and falling through to a return will be negligible.

Paul Sasik
  • 79,492
  • 20
  • 149
  • 189
  • That's not exactly what I was getting at...but that's a great idea...I'll definitely use it in the future. Much cleaner code! – bsara Feb 27 '12 at 22:43
  • The compiler in Release config would generate IL using the dictionary approach, but it'd be easier to maintain the switch block in the original question. – TimTIM Wong Jun 09 '22 at 17:05