public class Dummy
{
public event EventHandler SomethingHappened;
public void RaiseEvent()
{
try
{
SomethingHappened(this, EventArgs.Empty);
}
catch { }
}
}
As opposed to the recognised pattern of
public class Dummy
{
public event EventHandler SomethingHappened;
public void RaiseEvent()
{
if (SomethingHappened != null)
{
SomethingHappened(this, EventArgs.Empty);
}
}
}
Chatting to a friend at the pub, I mentioned about this code and the fact that the code was relying on exception handling to control the flow, and he mentioned that it would be a lot slower if there wasn't an event handler defined. I knew it would be slower to create, throw and catch a NullReferenceException than to perform a null check, but I hadn't realised just how much slower.To prove it, I created a class that contains a single event, and exposed two methods to raise the event - one with a try/catch block and one with a null check
public class Dummy
{
public event EventHandler SomethingHappened;
public void RaiseWithNullCheck()
{
if (SomethingHappened != null)
{
SomethingHappened(this, EventArgs.Empty);
}
}
public void RaiseInTryCatch()
{
try
{
SomethingHappened(this, EventArgs.Empty);
}
catch { }
}
}
...and a console app to repeatedly run the methods with no event handler defined
public static void Main(string[] args)
{
var dummy = new Dummy();
var stopwatch = Stopwatch.StartNew();
var iterations = 10000;
for (int i = 0; i < iterations; ++i)
{
dummy.RaiseWithNullCheck();
}
stopwatch.Stop();
Console.WriteLine("Null check: {0}", stopwatch.ElapsedMilliseconds);
stopwatch.Restart();
for (int i = 0; i < iterations; ++i)
{
dummy.RaiseInTryCatch();
}
stopwatch.Stop();
Console.WriteLine("Try catch: {0}", stopwatch.ElapsedMilliseconds);
Console.ReadLine();
}
This is a screenshot of the output - note the values are milliseconds
That's right. Sub-millisecond for the 10,000 null checks, and over 36 seconds for the 10,000 exceptions.The remaining question is "what's the difference in timing if there is a handler defined for the event?" Obviously in this case the try/catch will be quicker because there's no overhead of checking for null before invoking the event handler. I had to massage the code a little though, before the difference was noticeable:
public static void Main(string[] args)
{
var dummy = new Dummy();
// Add an empty event handler
dummy.SomethingHappened += (s, e) => { };
var stopwatch = Stopwatch.StartNew();
var iterations = 10000;
for (int i = 0; i < iterations; ++i)
{
dummy.RaiseWithNullCheck();
}
stopwatch.Stop();
// report in ticks, not milliseconds
Console.WriteLine("Null check: {0} ticks", stopwatch.ElapsedTicks);
stopwatch.Restart();
for (int i = 0; i < iterations; ++i)
{
dummy.RaiseInTryCatch();
}
stopwatch.Stop();
Console.WriteLine("Try catch: {0} ticks", stopwatch.ElapsedTicks);
Console.ReadLine();
}
Here's a typical output for this one (the timings vary a little between runs). Note that this is in ticks not milliseconds (a tick being 1/10,000 of a millisecond).
Given the tiny numbers when the event handler is defined, there isn't a good reason to rely on a try/catch when a simple null check will do.


No comments:
Post a Comment