Differing Returns

Because I enjoy IL so much, I thought I'd do a small test to see what the differences were, if any, in the IL of a given chunk of code. In this case, I was interested to see how the compilers generated IL based on how Return statements are structured. For example, look at these two methods in VB:

Public Function CheckX1(ByVal x As Integer) As Boolean
   
If (x < 0) Then
       
Return False
   
Else
       
Return True
   
End If
End Function

Public Function CheckX2(ByVal x As Integer) As Boolean
   
If (x < 0) Then
       
Return False
   
End If
   
Return True
End Function

Both methods do exactly the same thing and will produce exactly the same result, yet are written in a slightly different way. In my pre-.NET years, I wrote code like the first method and when I got into .NET, I started writing code like the second one. I'm not really sure why I changed my style, other than it just seems “cleaner“ to me now than it did when I was a wee lad.

So given these two methods, you'd expect the IL to be pretty damn close, if not the same (hoping for some compiler opimization). In VB, the IL is almost the same, with the first method containin 19 IL instructions and the second one containing 16 instructions. We don't get any optimization from the VB compiler on this, which is OK because we're talking nanoseconds here, but still, it would be nice to have.

Now let's take this example and turn it into C#, with the code as follows:

public bool CheckX1(int x)
{
   
if (x < 0)
   
{
       
return false;
   
}
   
else
   
{
       
return true;
   
}
}

public bool CheckX2(int x)
{
   
if (x < 0)
   
{
       
return false;
   
}
   
return true;
}

If you take this C# code, compile it, and look at the IL, you'd see that the IL for both methods is *exactly the same*. You'd also see that the number of IL instructions is 14, which is less than either one of the above VB methods. Unlike the VB compiler above, the C# compiler is doing some optimization for us which results is an ever-so-slight-hardly-even-noticeable performance gain.

So the purpose of this post was two-fold: 1) to show you that structuring your Return statements as found in the CheckX2 methods above results in slightly faster code and 2) don't always believe Microsoft when they tell you that all C# and VB code gets compiled down to the same IL.

Print | posted on Saturday, May 28, 2005 11:29 PM

Feedback

# re: Differing Returns

left by James Avery at 5/29/2005 2:56 PM
Great Article!

# Blog link of the week 21

left by Daniel Moth at 5/29/2005 3:40 PM
Blog link of the week 21

# Explicit assignment slower

left by Daniel Moth at 5/30/2005 9:59 AM
Explicit assignment slower

# re: Differing Returns

left by standardsGeek at 5/30/2005 6:02 PM
Of course all of these code fragments are bad form. You should only have one return from a method. Helps to keep maintainability metrics low.

# More Differing Returns

left by Dave Donaldson's Blog at 5/30/2005 7:37 PM

# c# vs vb.net

left by VB-tech weblog at 6/4/2005 3:23 PM

# re: Differing Returns

left by Willy Mejia at 6/6/2005 8:52 AM
How you compile? I compile without /optimize too, and I see that the first VB function has only ONE line more than the second VB function (and the functions of C#), this because the declared ELSE is evaluated, since from another way it return the default value (False), C# "eats" this step. On the other hand, the second function of VB is equal to both C# functions.

.method public instance bool CheckX1(int32 x) cil managed
{
// Code size 16 (0x10)
.maxstack 2
.locals init (bool V_0)
IL_0000: ldarg.1
IL_0001: ldc.i4.0
IL_0002: bge.s IL_000a
IL_0004: ldc.i4.0
IL_0005: stloc.0
IL_0006: br.s IL_000e
IL_0008: br.s IL_000e
IL_000a: ldc.i4.1
IL_000b: stloc.0
IL_000c: br.s IL_000e
IL_000e: ldloc.0
IL_000f: ret
} // end of method TestVB::CheckX1

.method public instance bool CheckX2(int32 x) cil managed
{
// Code size 14 (0xe)
.maxstack 2
.locals init (bool V_0)
IL_0000: ldarg.1
IL_0001: ldc.i4.0
IL_0002: bge.s IL_0008
IL_0004: ldc.i4.0
IL_0005: stloc.0
IL_0006: br.s IL_000c
IL_0008: ldc.i4.1
IL_0009: stloc.0
IL_000a: br.s IL_000c
IL_000c: ldloc.0
IL_000d: ret
} // end of method TestVB::CheckX2

These differences are reflected in the IL code (and it is necessary in Debug Mode). But if we compiled with /optimize+ (Release Mode), the IL code is EQUAL in the four functions...

.method public instance bool CheckX1(int32 x) cil managed
{
// Code size 8 (0x8)
.maxstack 2
.locals init (bool V_0)
IL_0000: ldarg.1
IL_0001: ldc.i4.0
IL_0002: bge.s IL_0006
IL_0004: ldc.i4.0
IL_0005: ret
IL_0006: ldc.i4.1
IL_0007: ret
} // end of method TestVB::CheckX1

So, While the Debug IL codes have some differences, the Release IL codes are equal. And that is the important thing, the Release Code.

# re: Differing Returns

left by Dave at 6/6/2005 9:29 AM
Willy: good observations all around and I'm glad you pointed them out. But a lot of people don't know about /optimize and some don't even know about Debug vs. Release mode. Maybe I should write something about that as well.
Comments have been closed on this topic.