Tests

Because Reflection is being used and slow/expensive at Runtime I did some testing

** Do not access or modify the UI, game objects, or anything that runs on Unity's main thread when using Async Events. An Exception will be thrown because Unity runs on the main thread and Async Events are called on potentially multiple threads depending on your system or the end user's system

A quick overview of terms used in the testing results tables below

  • Game Objects = An Object in Unity Scene, or class that inherits from MonoBehaviour

  • Normal Sync = Normal way of subscribing to events in a Synchronous manner

    public event Action<ILoginSession> LoggingIn;
    void Start()
    {
        LoggingIn += PlayerLoggingIn;
    }c
    private void OnApplicationQuit()
    {
        LoggingIn -= PlayerLoggingIn;
    }
    
    public void PlayerLoggingIn(ILoginSession loginSession)
    {
        Debug.Log($"Invoking Normal Event from {nameof(PlayerLoggingIn)}");
    }

  • Dynamic Sync = Subscribe to events using Synchronous Attributes - because methods are invoked dynamically and are not actual events / Action delegates, there is no need to unsubscribe the event

        [LoginEvent(LoginStatus.LoggingIn)]
        public void PlayerLoggingIn(ILoginSession loginSession)
        {
            Debug.Log($"Invoking Synchronous Event Dynamically");
        }

  • Dynamic Async = Subscribe to events using Asynchronous Attributes - because methods are invoked dynamically and are not actual events / Action delegates, there is no need to unsubscribe the event

    [LoginEventAsync(LoginStatus.LoggedIn)]
    public void LoginCallback(ILoginSession loginSession)
    {
        Debug.Log($"Invoking Async Event Dynamically from {nameof(LoginCallback)}");
    }

Dynamic Event Tests

Explanation of Test Tables Below

  • Blocks UI / Gameplay = If any method that is subscribed to an event does CPU intensive work (ex. for loop, foreach loop, synchronous call to FileSystem/IO, or synchronous HTTP call(web request)) the UI / Gameplay may pause and be unresponsive for a period of time. From the tests below a user will only notice this if you have many synchronous methods that are subscribed to Vivox Events. When possible, use Dynamic Async events or use Coroutines in Unity when using Synchronous Events to prevent Blocking/Unresponsiveness from the UI or Gameplay

  • For Loop - How many iterations of a for loop per method/event that was invoked. This is to simulate work being done after an event is fired. Example when a player joins Audio channel you loop thru some GameObjects or Instantiate them for the new player in the AudioChannel

  • Classes (1 event per class) = for every class on a game object there is only one method that is subscribed to a Vivox Event. Multiple classes will be on a game object to get more real-world results

  • Events Invoked = how many events were called for the test that was performed. Since every class only has 1 event being called/invoked then you can multiply game objects in test by the number of classes each game object has since every class only has 1 event.

  • Modify UI, Game Objects, call main thread = Depending on the type of event used, can I modify game objects? update the UI? (Unity UI is single threaded so async modifications to objects on the main thread will throw an exception. Also you may not receive an exception and the UI will still not update properly).

  • Seconds = how many seconds to complete/invoke all methods that have been subscribed to Vivox Events. Some tests took longer than 60 seconds as you will see below but most were more less than 60 so I kept the time interval as seconds

Game ObjectsEvents TypeBlocks UI / GameplayFor LoopClasses (1 event per class)Events InvokedModify UI, Game Objects, Call main thread Seconds
100

Normal Sync

1000

2

200

75.25 (1 min 15 seconds)

100

Dynamic Sync

1000

2

200

73.64 (1 min 13 seconds)

100

Dynamic Async

1000

2

200

33.42

null

100

Normal Sync

100

5

500

17.69

100

Dynamic Sync

100

5

500

21.50

100

Dynamic Async

100

5

500

8.05

Game ObjectsEvents TypeBlock UI / GameplayFor LoopClasses (1 event per class)Events InvokedModify UI, Game Objects, Call main threadSeconds
100

Normal Sync

1000

1

100

37.79

100

Dynamic Sync

1000

1

100

42.44

100

Dynamic Async

1000

1

100

18.18

null

null

null

100

Normal Sync

100

1

100

3.834

100

Dynamic Sync

100

1

100

4.078

100

Dynamic Async

100

1

100

1.835

Game ObjectsEvent TypeBlocks UI / GameplayFor LoopClasses (1 event per class)Events InvokedModify UI, Game Objects, Call main threadSeconds
500

Normal Sync

0

5

2,500

0.824

500

Dynamic Sync

0

5

2,500

1.171

500

Dynamic Async

0

5

2,500

0.349

null

1000

Normal Sync

0

5

5,000

1.832

1000

Dynamic Sync

0

5

5,000

1.934

1000

Dynamic Async

0

5

5,000

0.508

Game ObjectsEvent TypesBlocks UI / GameplayFor LoopClasses (1 event per class)Events InvokedModify UI, Game Objects, Call main threadSeconds

5,000

Normal Sync

0

1

5,000

1.622

5,000

Dynamic Sync

0

1

5,000

1.891

5,000

Dynamic Async

0

1

5,000

0.716

Based on the results if you are not updating game objects, the UI, or Unity Specific Work/Functionality on the main thread then I would use Async Events. If you need to use Synchronous events, then use coroutines inside your method (If it makes sense) for better performance and to avoid blocking the main thread.

For Async games/apps where most functionality is async/multi-threaded consider using the Unity Job System(with DOTS) or combine async Task/void methods with Parallel.For / Parallel.ForEach when doing CPU intensive work. Parallel.For / Parallel.ForEach will still block the UI / main thread if not used with async void or async Task methods. Do your own research if you don't know how to use asynchronous or Parallel programming. The advice I have mentioned above is not for all use cases and also considered bad in most contexts (like async void or using async with Parellel.ForEach), especially outside of Unity Game Engine where async is the standard for performant applications. Keep in mind Parallel programming (as well as async) comes with a lot of gotchas that could make your code hard to debug or actually make performance worse if not used correctly. Do your own research and avoid async until you have a good understanding of how to use it in Unity

Last updated