Delegates vs Observer Pattern

Hi Guys,

Has anyone tested the performance of calling a delegate vs an interface method aka written observer pattern in unity?

I’m quite interested in this but not able to test it right now.

It’s called the ‘Observer Pattern’, not ‘Observable Pattern’.

And technically speaking, a delegate conforms to the Observer Pattern. It’s an invokable object reference that a subject can notify.

from:

Subject is your class that has an event/delegate.

Observer is your delegate type.

ConcreteObserverA and ConcreteObserverB are any actual delegate instances you pass to the Subject (by event/delegate).

WITH THAT SAID

There are implications between implementing it with an interface vs a delegate.

One of them being garbage collection. Everytime you add/remove a delegate to an event/delegate field, there is some slight garbage generated because of the way delegates are implemented.

Where as if your concrete observer is an object that already exists, and just happens to have the interface implemented. You avoid garbage by not having to create a new object for registering. As long as the subject has a clean way of adding/removing observers though. Depending the collection used, and the way it’s called, it could be expensive.

In the end… unless you’re registering observers in the hundreds or thousands, or registering/unregistering over and over on a constant rate (every update)… you’re really not going to notice a huge difference.

And the nice thing about events/delegates is that they’re really easy to setup syntactically.

Heck, you can even see from Unity, they aren’t even consistent in how they implement their observer pattern. Using events in some places, interfaces in others, and messages in others.

Right it’s “observer”.
And yes, unity it selfe is inconsistent, thats a part of the reason I’m asking.

Delegate is a build-in feature and easy to use, I can even use lambda expression thats really great, but have I any disadvantage?
Allocate/deallocate memory by adding/removing methods? Thats not, because even with a common oberver pattern I’ll have the same memory issure maybe more plus the mapping of virtual methods.
The interesting part is the performance difference by calling a method via delegate and the notify method from the observer pattern.

Say you have an event like this:

public class Subject : MonoBehaviour
{
  
    public event System.Action Callback;
  
    public void SignalCallback()
    {
        if(this.Callback != null) this.Callback();
    }
  
}

And you say:

public class Observer : MonoBehaviour
{
  
    public Subject target;
  
  
    void Start()
    {
        target.Callback += CallbackReceiver;
    }
  
    void CallbackReceiver()
    {
        //do stuff
    }
  
}

What really happens is the compiler inserts the delegate constructor like so:

target.Callback += new System.Action(CallbackReceiver);

This means that a NEW action object exists now (delegates are objects).

Furthermore the underlying delegate ‘Callback’ has to store the entire delegate chain. Mono and .net do it slightly different, one as an array which means that a NEW array must also be created, where as the other uses a link list.

These objects exist on the heap, and may be short lived, creating ‘garbage’.

As opposed to the observer pattern. If in your observer pattern you dosomething like this:

public interface ICallback
{
  
    void Invoke();
  
}

public class Subject : MonoBehaviour
{

    private HashSet<ICallback> _callbacks = new HashSet<ICallback>();
  
    public void AddObserver(ICallback callback)
    {
        _callbacks.Add(callback);
    }
  
    public void RemoveObserver(ICallback callback)
    {
        _callbacks.Remove(callback);
    }
  
    public void SignalCallback()
    {
        var e = _callbacks.GetEnumerator();
        while(e.MoveNext())
        {
            e.Current.Invoke();
        }
    }
  
}

and add like so:

public class Observer : MonoBehaviour, ICallback
{
  
    public Subject target;
  
  
    void Start()
    {
        target.AddObserver(this);
    }
  
    void ICallback.Invoke()
    {
        //do stuff
    }
  
}

Because an instance of ‘Observer’ already exists. No NEW object is being created to add to the subject. So no stray objects there to pile up on the heap as garbage.

Furthermore because HashSet optimizes its collection resizing. There’s fewer allocations when adding new observers. Some still exist, but less than delegate… delegate is sadly an under-optimized collection.

As for mapping virtual methods. This isn’t as much a memory issue as just a slight slow down.

Thing is, because a delegate really is a dynamic method pointer, it too has some slight overhead when being called just like a virtual method does.

The difference is mostly negligible.

If the speed difference between calling an interface virtual method, vs a delegate method, is impacting your performance that much. You’ve got bigger issues.

If you truly want to know the difference… run a test. Call millions of virtual methods, then millions of delegate references, and see which performs better. I’m willing to bet they’re really close and won’t be noticed with out millions.

Go with a design you prefer. If you come from a heavy Java background, the interface method might seem more familiar. If you come from a C#/.net background the event might be better.

I’d go as far to say that the interface model is ever so slightly more efficient. But at the cost of a lot more boiler plate.

5 Likes

Under most circumstances, an interface appears to be faster than a delegate. I didn’t profile it, and since Unity is weird about its Mono implementation, it might have different performance results from .NET, but online results seem to be in agreement that a method call on an interface is faster. Since they also don’t result in an extra allocation, they’d be the superior choice for purely performance reasons. That said, I tend to favor delegates, because they can make for some very short, elegant, flexible solutions. When performance and garbage become a problem, I switch to an interface solution.

Test it.
Delegates are slower than observer.
The call performance of both are pretty close but adding and removing could be an issure for delegates and should be avoid every frame.

One key thing to bear in mind when considering exposing a delegate vs an interface is that a delegate has less exposure to nefarious meddling. If I have a reference to an object that implements some interface, I still have a reference to an object which can be cast and used nefariously. Whereas if I merely have a delegate, there is far less that I can do with it without using reflection.

For more clarity, I could easily see a novice team member doing something like the following:

private void SomeMethod(ICallback objectWithCallback)
{
    MonoBehaviour mb = objectWithCallback as MonoBehaviour;
    if (mb != null)
    {
        Object.Destroy(mb);
    }
}
1 Like

Your code would work that way, but you avoid problems when you code it the way UNITY intended. Events are more protected Delegates. You cannot override an Event. Further, you want to code an event as static to avoid having to have an instance to call, so correctly it looks like so:

using UnityEngine;
using System.Collections;

public class EventManager : MonoBehaviour
{
   public delegate void ClickAction();
   public static event ClickAction OnClicked;

   
   void OnGUI()
   {
       if(GUI.Button(new Rect(Screen.width / 2 - 50, 5, 100, 30), "Click"))
       {
           if(OnClicked != null)
               OnClicked();
       }
   }
}

See https://unity3d.com/learn/tutorials/topics/scripting/events?playlist=17117

What do you mean by the way Unity intended? What?

Yes, events are protected delegates. Did I say they weren’t?

You can’t override events… ok… did I say you could?

What?

No… wait… what?

No.

No.

I mean, static events are a plausible and useful thing. But they should only ever be static when you intend it to be static.

OK… think of it this way.

Lets say I have a Enemy script, and on it I have an ‘EnemyDied’ event to tell me when the enemy died.

public class Enemy: MonoBehaviour
{
 
    public event System.Action EnemyDied;
 
    public float Health = 10f;
 
    public void Strike(float damage)
    {
        if(this.Health <= 0f) return; //already dead
   
        this.Health -= damage;
        if(this.Health <= 0f)
        {
            if(this.EnemyDied != null) this.EnemyDied();
        }
    }
 
}

OK… now. Lets figure out what is different if ‘EnemyDied’ is static, as opposed to instance based.

If it’s instance based. I must get the reference to a specific instance to register the event handler. BUT also that handler will only be notified of the event when that specific instance dies.

Where as if it’s static based. I don’t have to get a reference to register the event handler. BUT the handler will be notified of the event any time ANY enemy dies.

Both have uses, yes. But it depends on which use I want that I would decide which to go with. If I’d like the granularity to listen for events on an instance by instance case. Then NO, static events are not the way I would go.

I’m not understanding what the intent of necroing this year old post was.

But if it was to implore that events should always be static, and instance level events are useless.

NO

DON’T DO THAT

That is an abuse of ‘static’.

Anyways the context of this thread is that of observer pattern (like that used in Java using interfaces) vs delegates. It had nothing to do with statics, your other points were not denied by anything I said, and my point through out the post is that C# events are basically an implementation of the observer pattern… using delegates.

3 Likes

@lordofduct I just wanted to say thanks for your explanations in this thread. It really helped clarify some research I’m doing atm!

@lordofduct Thank you. This is an very explanatory post. I’d also go for the “mistake” to go only for static events (simply because the usage in my mind asks for such a solution!) I wouldn’t have considered that other cases might demand the instantiated solution.