Somebody told me that Virtual Methods are slow.

And that this was why they "did not like them".

TL;DR

virtual calls in .Net are technically slow, for certain definitions of slow; Vampires are people too, and sometimes it's faster to walk.

In a world of monotonous horror, there could be no salvation in wild dreaming

This is the context of an ASP.Net Web Application written in C#. The discussion arose around TDD techniques whereby I was suggesting that we could make less use of Mocks and more use of overridden virtual methods on simple one-off subclasses for System Under Test (SUT) dependencies.

By this I mean that if we have something like this:

public interface ISomeDependency
{
     void DoSomething(int x);
}

meaning that an implementation might look like this:

public class SomeDependencyImplementation: ISomeDependency
{
    public virtual void DoSomething(int x)
    {
        ...//does whatever a SomeDependency does
    } 
}

Note the virtual bit there; this means now that if we want to test some SUT that uses an ISomeDependency to see how it deals with that dependency throwing an exception, then instead of writing this kind of thing:

var dep = Substitute.For();
dep.DoSomething(Arg.Any()).Throws(new InvalidOperationException());
var sut = new Thing(dep);
Assert.Throws(()=>sut.DoSomethingThatInvokesTheDep(1));

we could write this instead, which in my opinion makes the test a little more readable:

var dep = new ExplodingDependency();
var sut = new Thing(dep);
Assert.Throws(()=>sut.DoSomethingThatInvokesTheDep(1));

...
public class ExplodingDependency: SomeDependencyImplementation
{
    public override void DoSomething(int x)
    {
       throw new InvalidOperationException("boom!");
    }
}

Now in this contrived example there is not much to be gained (single method to mock means little is gained by the alternative) but the point was that sometimes we might want to have more nuanced behaviour in our dependencies for test purposes, which is tricky to setup via mocks. I'm not going to dive too deep into examples - that's for another post - but that was my reason for encouraging the use of virtual, or more to the point, encouraging that it not be banned or considered bad.

Patience, he told himself. Get yourself at least one virtue, anyway.

At the time I didn't want to argue too much; the person making the claim was - and is - an excellent, excellent software developer and had been very good to me as a mentor, and that's probably why I let it go. It did come to mind several times over the next couple of weeks, but I pushed it back down with a "meh, this is not something I have time to chew on right now".

But then a couple of months later I heard the same statement from someone else, who just so happened to be on a team I knew the original person was working with. And that statement was made in rebuttal to a suggestion I made for a simple way to get a really nasty class under test in short order so that it could be refactored, which it badly needed. Worse, the person making the argument couldn't even tell me why they were arguing against it.

The infection was spreading. I'm all for myths and legends but when they start to become dogma that is affecting the quality of code I draw the line. Something had to be done. I felt like I was the last virtual-lovin' man on earth, and it was time to do something about it.

How quickly one accepts the incredible if only one sees it enough

I decided it was time to put this misconception to the stake, so I wrote a test to prove my point.

And to my shock and horror, I discovered that virtual methods are in fact slow and that all this time I had in fact been murdering innocent people in their sleep and that meant that I WAS THE MONSTER ALL ALONG

(That all previous movie adaptations including the theatrical release of the most recent one apparently chose to miss the point baffles me. The alternative ending is much better in my opinion)

Anyway, here is the test code that shook my world:

using System;
using System.Diagnostics;

namespace VirtualsNotThatSlow
{
    class Program
    {
        static void Main(string[] args)
        {
            var timer=new Stopwatch();
            var runCount = (int)Math.Pow(10, 7);
            var nonVirtualTime = MeasureNonVirtualTime(runCount);
            var virtualTime = MeasureVirtualTime(runCount);

            Console.WriteLine($"{runCount} Non-Virtual calls took {nonVirtualTime} ms");
            Console.WriteLine($"{runCount} Virtual calls took {virtualTime} ms");
        }

        static long MeasureNonVirtualTime(int runCount)
        {
            var sw = new Stopwatch();
            var instance = new NonVirtualBaseClass();
            sw.Start();
            for (int x = 0; x < runCount; x++)
            {
                instance.DoSomething(x);
            }
            sw.Stop();
            return sw.ElapsedMilliseconds;
        }

        static long MeasureVirtualTime(int runCount)
        {
            var sw = new Stopwatch();
            //Force a virtual call
            BaseClassWithVirtual instance = new SubclassWithOverride();
            sw.Start();
            for (int x = 0; x < runCount; x++)
            {
                instance.DoSomething(x);
            }
            sw.Stop();
            return sw.ElapsedMilliseconds;
        }
    }

    class NonVirtualBaseClass
    {
        public int DoSomething(int x)
        {
            return x;
        }
    }

    class BaseClassWithVirtual
    {
        public virtual int DoSomething(int x)
        {
            return x;
        }
    }

    class SubclassWithOverride : BaseClassWithVirtual
    {
        public override int DoSomething(int x)
        {
            return x;
        }
    }
}

Nothing much to talk about here; we execute 10 Million calls on a non-virtual method, and then do the same on a virtual method. Here are the results from my modest dev machine, a 4 core Two-Point-SomethingOrOther Ghz thingy, running in Release mode:

dotnet run -c Release
10000000 Non-Virtual calls took 3 ms
10000000 Virtual calls took 24 ms

zOMG!!1! Virtual calls are 8 times slower than non-virtual calls!

A new superstition entering the unassailable fortress of forever. I am legend.

So there you go. I was wrong and I was the monster; If you are a post-apocalyptic non-virtual method calling mutant vampire who has had someone you know turned into dust by me whilst they slept, I offer you my sincerest apologies, and if it makes you feel any better I have reformed my ways, no longer watch Buffy and some of my best friends are bloodsucking mutants, well I don't actually hang out with them or call them ever but I totally would if they would stop trying to get into my house and eat me all the damned time.

HA HA NOPE: I TOLD YOU THE ALTERNATIVE ENDING IS BETTER

The time an individual virtual call took was 0.0000024 milliseconds. As opposed to 0.0000003 milliseconds for a non-virtual call. There are so many zeros there I lost interest in counting them.

I had to run 10 Million iterations just to measure it - 1 Million gave me 0 and 2 milliseconds respectively. It is literally impossible for a human being to tell the difference here.

What this means is that NOBODY CARES ABOUT THIS IMPERCEPTIBLE DIFFERENCE IN A WEB APP

This also means that we can feel free to use virtual methods in order that the poor bastard that has to get your crappy code under test doesn't end up going mad and staking people while they sleep.

Or in other words, if someone were to ask me this question:

"Hey Steve. I will be departing at 17:00 this Friday for Alpha Centauri and I want to optimise my travel time. I live about 2 km from the Spaceport. With that in mind, should I walk there or take a taxi?"

Then the correct answer is clearly walk because the traffic in town on Fridays is bloody murder.

Stephen Byrne

Read more posts by this author.