Dave Donaldson

Critical thinking in software development

Search

Advertisement

Subscribe

Follow Me

TwitterCounter for @arcware

My Tweets

  • Just went flying off the road. No lie. More soon.
  • Planning to watch the OSU-MichSt in the bar, if anyone wants to join, but will be late
  • Geeking out with @fallenrogue and @danhounshell on way to #codemash
  • Watching Spongebob while I wait for @fallenrogue and @danhounshell.
  • Re-syncing my entire 30GB Zune.

More Differing Returns

Tuesday, May 31 2005

In my previous “Differing Returns” post, a person named standardsGeek, whom I know and will deal with later ;-), left the following comment:

“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.”

I've heard this argument before, but I don't necessarily agree with the whole “maintainablity metrics“ thing. Well, maybe a little, but not as the sole purpose. And I certainly don't agree that the code in my previous post is “bad form“, and I'm not just saying that because I wrote it ;-)

But what I might agree with is if the comment was, “You should only have one return from a method because of performance reasons”. Hmmm... sounds like I need to write some code and investigate the IL again. So here we go.

Below are two more methods in VB that use a local variable to hold the value to be returned and both have only a single Return statement (see the previous post for CheckX1 and CheckX2):

Public Function CheckX3(ByVal x As Integer) As Boolean
   
Dim result As
Boolean

   
If (x < 0)
Then
       
result =
False
   
Else
       
result =
True
   
End
If

    Return result
End Function

Public Function CheckX4(ByVal x As Integer) As Boolean
   
Dim result As
Boolean

   
If (x > 0)
Then
       
result =
True
   
End
If

   
Return result
End Function

Both methods produce the exact same result, but the main difference between these two methods is that the CheckX4 method takes advantage of the default initialization the VB compiler performs for variables (in this case 'result' is implicitly set to False), thus allowing the If statement to be written differently from the CheckX3 method. For more on default initialization, see this post by Daniel Moth.

If you were to look at the IL for CheckX3, you'd find it contains 19 IL instructions and the IL is *almost* exactly the same as the IL of CheckX1. However, the interesting thing is the IL for CheckX4: it contains only 14 instructions, which is the same of the C# versions of CheckX1 and CheckX2.

Now if we flip the above code to C#, our code looks like this:

public bool CheckX3(int x)
{
   
bool result = false
;

   
if
(x < 0)
    {
        result =
false
;
    }
   
else
   
{
        result =
true
;
    }

   
return
result;
}

public bool CheckX4(int x)
{
    bool result = false
;

   
if
(x > 0)
    {
        result =
true
;
    }

   
return
result;
}

One thing to know is that C# does not have default initialization of variables, so I'm explicitly setting 'result' to false when I declare it. Given that info, the C# CheckX3 version is pointless; it just makes you write a couple extra lines of code and produces a few extra IL instructions, for a total of 18 (up from 14 for CheckX1 and CheckX2).

However, the IL for the C# CheckX4 contains 14 instructions - same as its CheckX1 and CheckX2 counterparts. The instructions themselves aren't exactly the same (almost), but that's not really the point anyway.

Armed with all this IL knowledge, it seems there are some takeaways:

  1. The VB compiler doesn't focus as much on optimization as the C# compiler does.
  2. Because of #1, when writing VB you gotta help the VB compiler out by taking advantage of its default initialization and try using a single Return statement wherever possible.
  3. Because of #1, when writing C# it doesn't necessarily matter if you have multiple Return statements.

 

Similar Posts

  1. Differing Returns
  2. Coding Guideline: DO Use an Underscore for Class Member Variables
  3. NHibernateRepository

11 comment(s) so far

The purpose of the single return statement has nothing to do with optimizing VB or C#. The key is that the return unconditionally ends execution of the method at that point. If you note in the documentation, it is equivalent to putting in "Exit Sub" or "Exit Function". Would you therefore argue that having multiple exit points on your methods is a good thing? Just asking. :)

I'd toss out several points, the first two straight from McConnell's /Code Complete/.



1) Multiple returns from a routine make sense when they enhance readability. My $0.02 is the extra variable and assignment are just cruft tossing in an extra step when looking through the code.



2) Guard clauses (early returns/exits) can simplify complex error handling.



3) Is this really an issue to worry about when considering performance? I'd argue that one should concentrate more on architecture and well-known bottlenecks rather than spending time and money furrowing one's brow about which loop controls or return methodologies to use. (Note I say that with no metrics in hand, just a bunch of reading of McConnell, Kernigan & Pike, etc.)



4) Your examples, especially tied with the IL, are great. But can one really draw "real world" performance conclusions based on your four small test cases? That's not a snarky comment, it's a serious question.

Jim - Here's my reponses to your points:



1. Yep, agreed.



2. Yep, agreed.



3. Yep, agreed, but with the understanding that part of architecture is the consideration of performance. Afterall, who wants a "perfectly" architected solution that doesn't perform or scale?



4. Excellent question. My answer is yes and no. My examples were for demonstration purposes only and aren't necessarily "real world"; however, the point wasn't about the real world. The point was about what's really happening under the covers. Most devs don't grok IL too much, so a lot of them might not understand that how they write and structure their Return statements *could* have a slight impact on performance. I also wanted to demonstrate the differences in the VB and C# compilers because the word from MS has always been the IL that is produced is the same. So I wanted to let people know that even in the simplest cases, that's not true.

Brian - First, let me say that I do not believe in the "only have 1 Return statement" mantra. I feel that sometimes you only need one, and other times you need to use multiple Return statements. No argument there.



As for your comment about Returns ending execution, yes I agree. But the line "The purpose of the single return statement has nothing to do with optimizing VB or C#" is kind of missing the point. I wanted to demonstrate that even in the simplest cases, code written in VB and then exactly the same in C# does NOT produce the same IL because the C# compiler performs some optimizations that the VB compiler doesn't. I was just using Return statements as an example.

I did catch your main point being about looking under the covers with IL, especially what really happens vs. what MS says happens. Good illustration!



My basis for #3 (#4, partially) was an interesting talk by McConnell at last year's SD West. He hit hard the disconnect between long-held /perceptions/ vs. actual metrics on optimization and performance. He noted that metrics were pointing out developers were missing the boat something like 95% of the time when trying to figure out what to optimize. He also hit hard the need for sorting out perceptions abou where performance impacts are, especially on larger scale problems.



That said, this isn't necessarily optimization, but development using sound principles, so its impact may not be quite the same.

Jim - again, good points all around. I'm just hoping that my demonstration of a trivial example gets devs to take similar actions (with IL) for more complex and larger codebases.



And I'm jealous you got to see McConnell talk in person :-)

"And I'm jealous you got to see McConnell talk in person"



Watershed event in my life. His talk, plus all the other excitement at SD West, was the first kick in my hindquarters to get out of a boring, draining line of work and get back into technical stuff.

Really interesting stuff! If I get some time, I'd like to see what IL this results in:



public bool CheckX5(int x)


{


return (x > 0);


}

Not is by an optimization problem by which the IL code of the VB compiler is "longer", It's by the feature of compatibility of the language... Anyway, the VB compiler also focuses in the optimization when it must do it, in the Release Mode.



On the other hand, because the "default initialization" in VB a CheckX5 function can write:



Public Function CheckX4(ByVal x As Integer) As Boolean


If (x > 0) Then Return True


End Function



Resultant IL Code (Debug Mode):



.method public instance bool CheckX5(int32 x) cil managed


{


// Code size 10 (0xa)


.maxstack 2


.locals init (bool V_0)


IL_0000: ldarg.1


IL_0001: ldc.i4.0


IL_0002: ble.s IL_0008


IL_0004: ldc.i4.1


IL_0005: stloc.0


IL_0006: br.s IL_0008


IL_0008: ldloc.0


IL_0009: ret


} // end of method TestVB::CheckX5



More optimized that any C# function. But again, the IL Code in Release Mode is "equal" that anyone of the CheckX1 & CheckX2 functions in VB or C#...



And peculiarly, in Release Mode, the VB functions CheckX3 & CheckX4 have more "optimized" IL Code than the same C# functions, but that is another story...


Oops... I wrote CheckX4 as the function name, but it must to be CheckX5:



Public Function CheckX5(ByVal x As Integer) As Boolean


If (x > 0) Then Return True


End Function



Sorry.



Wouter wrote on Friday, March 31 2006

how do the compilers handle



Public Function CheckX6(ByVal x As Integer) As Boolean


Return (x > 0)


End Function


Post your comment

Comment