How fast are C# delegates and events? The best answer is “compared to what?”. You probably use callbacks all the time, but what’s the fastest kind of callback? C# delegates and events are nice, built-in features of C#, but you could also implement your own callback interface. Would that gain you any speed? Read on for the performance test and results!

First let’s take a quick look at today’s contenders. First up is the lowly delegate:

// Define the type of function that the delegate calls
delegate void Delegate();
 
// Create a delegate that can point to one ore more functions
Delegate del;
 
// Point the delegate at a single callback function
void Foo() {}
del = Foo;
 
// Point the delegate at multiple callback functions
void Goo() {}
del = Foo;
del += Goo;
 
// Call all the functions the delegate points to
del();

C# events are built directly on top of delegates:

// Create an event that can point to one ore more functions
event Delegate ev;
 
// Point the event at a single callback function
void Foo() {}
ev = Foo;
 
// Point the event at multiple callback functions
void Goo() {}
ev = Foo;
ev += Goo;
 
// Call all the functions the event points to
ev();

The last one is an odd-ball character for C#, but extremely common in other languages. For example, Java’s ActionListener and other “listeners” use this strategy. Simply put, you define an interface instead of a delegate and then implement a class instead of using a callback function. Here’s how that looks:

// Define an interface instead of a delegate
interface ICallback
{
    void Call();
}
 
// Define a class instead of a callback function
class Callback
{
    public void Call()
    {
    }
}
 
// Define a callback for a single function
ICallback callback;
 
// Point the callback at a single function
callback = new Callback();
 
// Call a single callback
callback.Call();
 
// Define a callback for multiple functions
ICallback[] callbacks;
 
// Point the callback at multiple functions
callbacks = new ICallback[]{ new Callback(), new Callback() };
 
// Call multiple callbacks
for (var i = 0; i < callbacks.Length; ++i)
{
    callbacks[i].Call();
}

The last one is much less convenient as you’re left to create the lists of callbacks, handle adding and removing during the callback process, and awkwardly define classes for each callback function. All without the help of Java’s anonymous classes:

// This is Java code. You can't do this in C#.
callback = new ICallback() {
    public void Call() {
    }
};

That said, all three options are valid ways to implement a callback scheme. So, which is fastest? The following test finds out with a single, small MonoBehaviour:

using UnityEngine;
 
public interface ICallback
{
	void Call();
}
 
public class Callback : ICallback
{
	public void Call()
	{
	}
}
 
public delegate void Delegate();
 
public class TestScript : MonoBehaviour
{
	private Delegate DelegateSingle;
	private Delegate DelegateMultiple;
	private event Delegate EventSingle;
	private event Delegate EventMultiple;
	private ICallback CallbackSingle;
	private ICallback[] CallbackMultiple;
 
	private const int NumIterations = 100000000;
	private const int MultipleCount = 10;
 
	private string report;
 
	void Start()
	{
		Setup();
		Test();
	}
 
	private void Setup()
	{
		DelegateSingle = CallbackFunction;
 
		DelegateMultiple = CallbackFunction;
		for (var i = 0; i < MultipleCount-1; ++i)
		{
			DelegateMultiple += CallbackFunction;
		}
 
		EventSingle = CallbackFunction;
 
		EventMultiple = CallbackFunction;
		for (var i = 0; i < MultipleCount-1; ++i)
		{
			EventMultiple += CallbackFunction;
		}
 
		var callback = new Callback();
 
		CallbackSingle = callback;
		CallbackMultiple = new ICallback[MultipleCount];
		for (var i = 0; i < MultipleCount; ++i)
		{
			CallbackMultiple[i] = callback;
		}
	}
 
	private void Test()
	{
		var stopwatch = new System.Diagnostics.Stopwatch();
 
		stopwatch.Start();
		for (var i = 0; i < NumIterations; ++i)
		{
			DelegateSingle();
		}
		var delegateSingleTime = stopwatch.ElapsedMilliseconds;
 
		stopwatch.Reset();
		stopwatch.Start();
		for (var i = 0; i < NumIterations; ++i)
		{
			DelegateMultiple();
		}
		var delegateMultipleTime = stopwatch.ElapsedMilliseconds / MultipleCount;
 
		stopwatch.Reset();
		stopwatch.Start();
		for (var i = 0; i < NumIterations; ++i)
		{
			EventSingle();
		}
		var eventSingleTime = stopwatch.ElapsedMilliseconds;
 
		stopwatch.Reset();
		stopwatch.Start();
		for (var i = 0; i < NumIterations; ++i)
		{
			EventMultiple();
		}
		var eventMultipleTime = stopwatch.ElapsedMilliseconds / MultipleCount;
 
		stopwatch.Reset();
		stopwatch.Start();
		for (var i = 0; i < NumIterations; ++i)
		{
			CallbackSingle.Call();
		}
		var callbackSingleTime = stopwatch.ElapsedMilliseconds;
 
		stopwatch.Reset();
		stopwatch.Start();
		for (var i = 0; i < NumIterations; ++i)
		{
			for (var j = 0; j < MultipleCount; ++j)
			{
				CallbackMultiple[j].Call();
			}
		}
		var callbackMultipleTime = stopwatch.ElapsedMilliseconds / MultipleCount;
 
		report =
			"Test,Single Time,Multiple Time\n"
			+ "Delegate," + delegateSingleTime + "," + delegateMultipleTime + "\n"
			+ "Event," + eventSingleTime + "," + eventMultipleTime + "\n"
			+ "ICallback," + callbackSingleTime + "," + callbackMultipleTime;
	}
 
	void OnGUI()
	{
		GUI.TextArea(new Rect(0, 0, Screen.width, Screen.height), report);
	}
 
	private void CallbackFunction()
	{
	}
}

If you want to try out the test yourself, simply paste the above code into a TestScript.cs file in your Unity project’s Assets directory and attach it to the main camera game object in a new, empty project. Then build in non-development mode for 64-bit processors and run it windowed at 640×480 with fastest graphics. I ran it that way on this machine:

  • 2.3 Ghz Intel Core i7-3615QM
  • Mac OS X 10.10.3
  • Unity 5.0.1f1, Mac OS X Standalone, x86_64, non-development
  • 640×480, Fastest, Windowed

And got these results:

Test Single Time Multiple Time
Delegate 301 742
Event 255 749
ICallback 229 274

Callbacks Performance Graph

All three approaches have fairly similar times when only calling a single callback. This is probably the most common case, so it’s good to see that the easier-to-use approaches—delegates and events—are nearly as fast as the trickier one: interfaces. That said, interfaces was the fastest followed by events (11% slower) and then delegates (31% slower).

The big difference is when multiple (10) functions were called by each. Interfaces barely got slower then the single version. On the other hand, delegates and events took more than twice as long. Clearly there is some optimization for single function delegates and events. Interfaces are the big winner here are delegates are 271% slower and events are 273% slower.

Now that we’ve established which is faster—interfaces—the question becomes: should you bother? Interfaces are definitely more work to set up, harder to read, more error-prone, and more difficult to integrate into your code. The percentages faster are impressive, but will you save a lot of total CPU time? The above test results are measured in milliseconds per 100 million callback calls. That’s clearly way more than you’ll have in any reasonable game or app.

Say your game had a thousand callbacks per frame. That’d be a lot of callbacks, but still 100,000 times less than this article’s test results show. Even in the slowest case—events with multiple callback functions—you’d only be using 0.00749 milliseconds to call all the callbacks. That’s a very small slice of time and probably won’t ever matter to your overall frame rate. This means you can safely skip the interfaces optimization unless you find yourself wanting to call at least hundreds of thousands of callbacks per frame. Since that’ll probably never happen, you can probably skip interfaces as a callback mechanism altogether.

Got another way to do callbacks? Got a preference for one way over another? Sound off in the comments!