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