How method length Affects performance in .net

Several months ago I was having a discussion (that’s a polite word for argument) with a few co-workers about the optimal length of methods and the topic of performance came up.

Someone with a C++ background brought up the point that shorter methods were less efficient than longer ones due to the extra cost of more V-Table lookups.

At the time, I countered that the performance differences were surely minimal and that the trade-off was justifiable when taking into account the maintainability benefits of shorter methods (increased readability, decreased chance of bugs, etc.).

However, I recently discovered while reading a chapter in Bill Wagner’s Effective C# book that this seemingly common-sense assertion about the way method length affects performance is not necessarily true in the .NET world.

The primary reason has to do with how JIT compilation works, which is another round of compilation that occurs at runtime in order to transform code from IL to machine language.

In order to amortize the startup cost over the life of an application, the CLR compiles code on a method by method basis the first time it is used.

The ultimate result is that longer methods may slow down an application by requiring code to be prematurely compiled before it is actually used.

Consider the following example:

   1: public void DoFunStuff(bool isBossPresent)
   2: {
   3:     if (isBossPresent)
   4:     {
   5:         //do 50 lines worth of crazy shit inline
   6:     }
   7:     else
   8:     {
   9:         //do 50 lines of tamer stuff
  10:     }
  11: }

Regardless of what parameter is passed to the method, all 100+ lines of code will have to be JIT compiled the first time the method is used.

Now consider this refactored version:

   1: public void DoFunStuff(bool isBossPresent)
   2: {
   3:     if (isBossPresent)
   4:     {
   5:         DoCrazyShit();
   6:     }
   7:     else
   8:     {
   9:         DoRelativelyTameStuff();
  10:     }
  11: }

The first time this version of the method is called, the JIT compilation process will be faster because only half as many lines of code need to be compiled.

If true is passed in to this method as a parameter then the CLR will compile everything under the DoCrazyShit method, but will delay compiling the DoRelativelyTameStuff method until the DoFunStuff method is called with a false parameter.

Reducing the number of lines of code that need to be compiled is not the only benefit of shorter methods.

Small, simple methods also facilitate the use of other performance boosting techniques like enregistration (storing variables in the register instead of on the stack) and method inlining (substituting method calls for the implementation).

The moral of the story? Keep the JIT compiler, impatient users, and fellow developers who have to maintain your crap after you retire to some tropical island happy. Keep your methods short!

18 Comments

  1. Raghuraman August 15, 2010 9:26 pm 

    Quite interesting and valuable.

    Thx

    Raghu

  2. AASoft August 15, 2010 9:32 pm 

    What happens if you NGEN your assembly? AFAIK, the JITter wouldn’t need to run for these assemblies, since they’re already, well, JITted by NGEN.

  3. Cory Fowler August 15, 2010 10:12 pm 

    This should be fun to send around the office, thanks!

  4. Igor Zevaka August 15, 2010 10:35 pm 

    I would seriously doubt the C programmer’s practical knowledge of performance if they consider virtual function overhead at all significant.

    Perhaps in the 80′s that was the case, but these days virtual function invocations are really close to one CPU instruction overhead over non-virtual functions.

  5. Igor Zevaka August 15, 2010 10:37 pm 

    And when I say C I mean C Plus Plus. I think the ” “(plus plus) got swallowed by WordPress.

  6. Al Gonzalez August 15, 2010 10:46 pm 

    Not an advocate for long functions, but the look up overhead is there everytime you run the app; while the JIT overhead mentioned here only happens on your first run through.

  7. Dewayne Christensen August 16, 2010 12:23 am 

    Igor .

    I suppose the guy writes i >> 1 instead of i / 2 too.

  8. Dewayne Christensen August 16, 2010 12:24 am 

    (That’s supposed to be Igor-plus-plus. What’s up with the plus-eater?)

  9. Pete Austin August 16, 2010 3:47 am 

    If this issue was at all significant, Microsoft would have amended JIT compilation to work at the block level.

    It’s better to make your code straightforward and understandable, then use part of the time saved in maintenance to find and optimize the slow parts

  10. Damien Wintour August 16, 2010 6:27 am 

    Nice article. Now there’s no excuse to write lengthy methods with high cyclomatic complexity. As a Technical Lead you also want to know about such functions so best add a report to your CI build that shows you the worst offenders. Works for me.

  11. Russell Ball August 16, 2010 10:03 am 

    @AASoft – Yes, I assume that NGening the assemblies would avoid this cost. However, there are also several potential downsides (including performance) to using NGen that Jeffrey Richter talks about in this article http://www.codeguru.com/Csharp......php/c4651.

  12. Russell Ball August 16, 2010 10:08 am 

    @Al Gonzalez – That is a good point that I wondered about myself. However, I would say that long cold-start times can also be extremely annoying from the user perspective and important to pay attention to as a developer.

    It’s similar to the scenario of spending a lot of effort to cover ourselves from a scalability point of view even though it slightly slows down the average response time and it is only meant to remedy the less than 1% of the time when severs actually get stressed from too many simultaneous requests.

  13. Russell Ball August 16, 2010 10:09 am 

    @Dewayne – if there’s one rule I live by, it’s that you can never trust a second plus.

  14. Russell Ball August 16, 2010 10:20 am 

    @Igor – Thanks for the info about virtual function overhead.

    To be fair, I don’t think my co-worker was advocating that we should only do long functions because of this overhead. I think he was just throwing out reasons why it may not be a good idea to do things like 1 line methods, which I occasionally do if it makes code more readable.

    Also, I really should not have specifically mentioned V-Table lookup. I really meant the extra overhead of calling any function, not just virtual methods.

  15. AASoft August 17, 2010 11:43 pm 

    @Russell Ball
    Not advocating NGEN for all cases, of course. But, if the assembly is NGENed at the client’s machine (e.g. after installation of your application), there aren’t many downsides (that I can see). This part: “Inferior Load-Time Performance (Rebasing).” is interesting, so I’ll read more about that, but apart from that, I don’t see other downsides (assuming that your installer properly updates/removes assemblies from the GAC). That said, it is, of course, better to test performance to see whether NGENed assemblies actually perform better in each specific case.

  16. writingfavor.com June 6, 2013 4:29 am 

    You sure have a gift for writing such great articles! Thank you!

Leave a Reply