More efficient unity events

Unity Events are great tool to keep your classes decoupled. But their performance is worse than C# events.
I’m not expecting to achieve the same performance as C# native events with Unity Events. I want to know if there is anything that I can do to improve the performance of Unity Events.

It is not that simple. UnityEvents can be worse than native C# events, but not if you have more than one listener. And obviously it depends on what measurement, time or garbage?
The best way if you do it through interfaces. Example here: JacksonDunstan.com | Event Performance: C# vs. UnityEvent

Thank you for your response.
A big feature of Unity Events is that they are assignable through inspector. And you can add listeners without touching the code. But in the interface version it is required to change every class that wants to be a listener to a specific event.

As I said, I’m just looking for general optimization tips (if there are any). On both time and garbage.

Do you use these events for more than occasional “button press events” for example? For what things do you use them that makes it worthy to optimize?

1 Like

We have different kind of events in our game. Some of them are occasional, some others happen in quick successions for a short amount of time, Some happen at constant rate, etc.

I don’t know which one of them is good to be converted to Unity Events. What kind of events are best for Unity Events? In what conditions they work “optimally”?

I use Unity Events pretty much for UI only, button presses and things like that.

When Unity Events was new, I recall I used them for a method that was called every frame, but noticed every call allocates garbage memory. Since then I never tried to use Unity Events for more than occasional buttons clicks. I don’t know if they fixed the garbage issue, I believe it was around Unity 4.6 or perhaps 5.0.

If it’s important to be able to wire method calls through the Inspector, then it probably makes sense to use Unity Events. But if it’s slow, generates garbage and it turns out to be a problem, then I would look for alternatives.

Regular C# events come to mind. However, if add and remove events often during Update, it also generated garbage as far as I recall. Depending on the context, I often prefer interfaces over delegates, just because adding and removing them to a list does not generate garbage and I find calling methods through an interface easier to debug than delegates.

I went into a bit more detail on it in this thread: World Manager - Generic world management system

Just search for “We moved away from using this kind of event subscription” and then you found my explanation.

1 Like

I did some tests. Here are my scripts:

using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Profiling;

public class Listener : MonoBehaviour
{
    [SerializeField] private int unitySum;
    [SerializeField] private int normalSum;

    public void AddUnityListeners()
    {
        Profiler.BeginSample("AddingUnityEventListener");
        var unityEventTest = GetComponent<UnityEventTest>();
        UnityAction<int> unityAction = UnityCall;
        for (int i = 0; i < 1000; i++)
        {
            unityEventTest.Test.AddListener(unityAction);
        }

        Profiler.EndSample();
    }

    public void AddNormalListeners()
    {
        Profiler.BeginSample("AddingNormalEventListener");
        var normalEventTest = GetComponent<NormalEventTest>();
        NormalEventTest.Method method = NormalCall;
        for (int i = 0; i < 1000; i++)
        {
            normalEventTest.Test += (method);
        }

        Profiler.EndSample();
    }

    private void UnityCall(int arg0)
    {
        unitySum += arg0;
    }

    private void NormalCall(int arg)
    {
        normalSum += arg;
    }
}
using System;
using UnityEngine;
using UnityEngine.Events;

public class UnityEventTest : MonoBehaviour
{
    [Serializable]
    public class SomeEvent : UnityEvent<int>{}

    public SomeEvent Test;

    public void Update()
    {
        Test.Invoke(5);
    }
}
using UnityEngine;

public class NormalEventTest : MonoBehaviour
{
    public delegate void Method(int arg);

    public event Method Test;

    private void Update()
    {
        if (Test != null)
        {
            Test(5);
        }
    }
}

And these are the results:

Adding 1000 Unity Event listeners:    39 KB allocated memory.    2.6 ms
Adding 1000 Normal Event listeners:   4 MB allocated memory.     10.9 ms
Invoking Unity Event in every frame:  0 allocated memory.        2.5 ms (Per frame)
Invoking Normal Event in every frame: 0 allocated memory.        0.25 ms (Per frame)

Unity devs have done a really good job with Unity Events.
~2 ms in invoking Unity Events is because of null checks that unity does. It’s probably what you want anyway. You don’t want to get missing references when calling events.

In conclusion, Unity Events are already really high performance. I don’t think that you can get any faster with these kind of events.

9 Likes

Sorry to necropost, but seriously - these figures literally show native C# events being 10x faster than Unity events so the phrase “Unity Events are already really high performance. I don’t think that you can get any faster with these kind of events.” is somewhat misleading.

(I came here to find exactly this info, so thanks for posting it, but it’s native C# events for me, every time!

Please note that it’s not that unity events are slow it’s delegates are very fast. I run same test in build a while ago and unity events is roughly 11x slower than delegate but both GetComponent and changing position are 4x slower than unity events and toggling game object activate state is 6-7 slower than that. So toggling active state is roughly 300 slower than delegate call.

2 Likes