Greg Beech's Website

Static vs instance method performance

 

I'm sick of the static vs instance methods argument based on performance. Performance has nothing to do with it. You should make your methods either static or instance depending on the way the method is used and what is more intuitive to the user of the class. Even Microsoft are guilty of pushing the performance agenda with an FxCop rule which moans about the 'this' pointer not being used so the method should be static, which goes against the tool's stated aim of helping to make class libraries more interoperable and intuitive to use.

Last week I saw a question on a newsgroup from somebody asking whether he should make his business layer methods static as he had heard they are faster than instance methods, and wanted to make his web service quicker. If you're reading chunks of XML data over the internet, deserializing them into objects, processing the data (probably involving a database call or two), creating response objects, serializing those into XML, and transmitting it over the internet, do you really think that making methods static is going to make any difference?

So let's have a look at the actual difference between calling and instance and a static method by doing some performance testing. I wrote a little application which is shown below, compiled it in release mode, then ran it three times to get some results and ensure they were valid. There's nothing special about this application other than the usage of the MethodImpl attribute which prevents the JIT compler from inlining (i.e. optimising away) the empty method so we know it actually gets called each time the loop runs.

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

class Program
{
    static void Main(string[] args)
    {
        // pre-jit any methods used in the test
        Sample.Foo();
        Stopwatch timer = Stopwatch.StartNew();
        timer.Stop();
        timer.Reset();

        // run the test
        timer.Start();
        for (int i = 0; i < int.MaxValue; i++)
        {
            Sample.Foo();
        }
        timer.Stop();

        // display results
        Console.WriteLine(timer.ElapsedMilliseconds);
        Console.ReadLine();
    }
}

class Sample
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    public static void Foo()
    {
    }
}

The three runs gave the following times on my machine: 7678ms, 7704ms and 7699ms, which averages out to 7694ms to call a method approximately two billion times. Next I changed the application to use an instance method, to see whether this would be any slower:

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

class Program
{
    static void Main(string[] args)
    {
        // pre-jit any methods used in the test
        Sample sample = new Sample();
        sample.Foo();
        Stopwatch timer = Stopwatch.StartNew();
        timer.Stop();
        timer.Reset();

        // run the test
        timer.Start();
        for (int i = 0; i < int.MaxValue; i++)
        {
            sample.Foo();
        }
        timer.Stop();

        // display results
        Console.WriteLine(timer.ElapsedMilliseconds);
        Console.ReadLine();
    }
}

class Sample
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    public void Foo()
    {
    }
}

The times from this program are 7676ms, 7709ms and 7712ms, which averages out to 7699ms. So in calling a method two billion times you could save yourself around 5ms by making it static, which is a saving of approximately nothing for each method call.

Now one of the interesting things about the C# compiler is that it makes all instance method calls using the callvirt instruction rather than just call, to force a null pointer check on the object before calling the method. However, as we know the object is not null here, I thought I would see what happened to the times if I recompiled the MSIL using call instead... could we get any of those elusive milliseconds back? A quick ILDASM MyApp.exe /OUT=MyApp.il, an edit with notepad, and an ILASM MyApp.il /RESOURCE=MyApp.res later, I was able to run the new application and see what the times were: 7716ms, 7693ms and 7712ms, which averages out to 7707ms. Oh no! We lost another 8ms by using the call instruction.

Of course, we can't really time things down to the millisecond as there are other programs running on my computer which will be consuming small but not insignificant amounts CPU time. So we can essentially say that it takes the same amount of time to call either a static or an instance method. Yes, there are cases I haven't covered here such as virtual methods, passing parameters and getting return values, so feel free to use my program to experiment. I don't think you'll find a great deal of difference though - certainly not enough to sway your design decisions.

And the FxCop rule? Well it's still useful as it does sometimes point out methods which make more semantic sense as static ones, however you shouldn't just act on its recommendation without thinking about it. Make your methods static or instance depending on which will be most intuitive for users of your class.


Posted Jan 11 2007, 06:00 PM by Greg Beech

Comments

Nikola Malovic wrote re: Static vs instance method performance
on 01-03-2008 12:35 AM

Well, if you leave the instantiation outside of the loop then there is no other difference then the callvirt instruction which is not significant and one small GC instance collection.

But, the real difference to me from performance perspective is that in case you don't know who would be using your method, having instance method could elude some DEVs to put that instantiation inside of the loop (e.g. to send some parameters through constructor - stupid example but seen that a few times) when you would then have serious hits.

Declaring method as static, on other hand guarantee you that there is no chance for that kind of misuse.

All this been said, I still vote against static methods because architecturally they limit a lot number of options available (no inheritance, interfaces etc..)

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright (C) Greg Beech. All rights reserved.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems