# Tests

&#x20;Because **Reflection** is being used and slow/expensive at Runtime I did some testing

{% hint style="danger" %}
\*\* <mark style="color:red;">**Do not access or modify**</mark> 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
{% endhint %}

**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<br>

  ```csharp
  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 <mark style="color:green;">**not actual events**</mark> <mark style="color:green;"></mark><mark style="color:green;">/</mark> <mark style="color:green;"></mark>*<mark style="color:green;">**Action delegates,**</mark>* there is no need to unsubscribe the event&#x20;

  ```csharp
      [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 <mark style="color:green;">**not actual events**</mark> <mark style="color:green;"></mark><mark style="color:green;">/</mark> <mark style="color:green;"></mark>*<mark style="color:green;">**Action delegates,**</mark>* there is no need to unsubscribe the event&#x20;

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

## Dynamic Event Tests

### &#x20;Explanation of Test Tables Below

* **Blocks UI / Gamepla**y = If any ***method*** that is subscribed to an event does ***CPU*** intensive work (ex. <mark style="color:blue;">for</mark> loop, <mark style="color:blue;">foreach</mark> 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 <mark style="color:red;">**Synchronous Events**</mark> <mark style="color:red;">to prevent</mark> <mark style="color:red;"></mark>*<mark style="color:red;">Blocking/Unresponsiveness from the UI or Gameplay</mark>*
* **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, *<mark style="color:purple;">**can I modify game objects?**</mark>* *<mark style="color:purple;">**update the UI?**</mark>* (<mark style="color:red;">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</mark>).&#x20;
* **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

<table><thead><tr><th width="110" data-type="number">Game Objects</th><th width="103">Events Type</th><th width="116" data-type="checkbox">Blocks UI / Gameplay</th><th width="73">For Loop</th><th width="101">Classes (1 event per class)</th><th width="93">Events Invoked</th><th width="150" data-type="checkbox">Modify UI, Game Objects, Call main thread </th><th width="204">Seconds</th></tr></thead><tbody><tr><td>100</td><td>Normal Sync</td><td>true</td><td>1000</td><td>2</td><td>200</td><td>true</td><td>75.25 (1 min 15 seconds)</td></tr><tr><td>100</td><td>Dynamic Sync</td><td>true</td><td>1000</td><td>2</td><td>200</td><td>true</td><td>73.64 (1 min 13 seconds)</td></tr><tr><td>100</td><td>Dynamic Async</td><td>false</td><td>1000</td><td>2</td><td>200</td><td>false</td><td>33.42</td></tr><tr><td>null</td><td></td><td>false</td><td></td><td></td><td></td><td>false</td><td></td></tr><tr><td>100</td><td>Normal Sync</td><td>true</td><td>100</td><td>5</td><td>500</td><td>true</td><td>17.69</td></tr><tr><td>100</td><td>Dynamic Sync</td><td>true</td><td>100</td><td>5</td><td>500</td><td>true</td><td>21.50</td></tr><tr><td>100</td><td>Dynamic Async</td><td>false</td><td>100</td><td>5</td><td>500</td><td>false</td><td>8.05</td></tr></tbody></table>

<table><thead><tr><th width="111" data-type="number">Game Objects</th><th width="107">Events Type</th><th data-type="checkbox">Block UI / Gameplay</th><th width="75" data-type="number">For Loop</th><th width="102">Classes (1 event per class)</th><th data-type="number">Events Invoked</th><th width="115" data-type="checkbox">Modify UI, Game Objects, Call main thread</th><th>Seconds</th></tr></thead><tbody><tr><td>100</td><td>Normal Sync</td><td>true</td><td>1000</td><td>1</td><td>100</td><td>true</td><td>37.79</td></tr><tr><td>100</td><td>Dynamic Sync</td><td>true</td><td>1000</td><td>1</td><td>100</td><td>true</td><td>42.44</td></tr><tr><td>100</td><td>Dynamic Async</td><td>false</td><td>1000</td><td>1</td><td>100</td><td>false</td><td>18.18</td></tr><tr><td>null</td><td></td><td>false</td><td>null</td><td></td><td>null</td><td>false</td><td></td></tr><tr><td>100</td><td>Normal Sync</td><td>true</td><td>100</td><td>1</td><td>100</td><td>true</td><td>3.834</td></tr><tr><td>100</td><td>Dynamic Sync</td><td>true</td><td>100</td><td>1</td><td>100</td><td>true</td><td>4.078</td></tr><tr><td>100</td><td>Dynamic Async</td><td>false</td><td>100</td><td>1</td><td>100</td><td>false</td><td>1.835</td></tr></tbody></table>

<table><thead><tr><th width="109" data-type="number">Game Objects</th><th width="99">Event Type</th><th width="117" data-type="checkbox">Blocks UI / Gameplay</th><th width="74">For Loop</th><th>Classes (1 event per class)</th><th width="96">Events Invoked</th><th width="152" data-type="checkbox">Modify UI, Game Objects, Call main thread</th><th>Seconds</th></tr></thead><tbody><tr><td>500</td><td>Normal Sync</td><td>true</td><td>0</td><td>5</td><td>2,500</td><td>true</td><td>0.824</td></tr><tr><td>500</td><td>Dynamic Sync</td><td>true</td><td>0</td><td>5</td><td>2,500</td><td>true</td><td>1.171</td></tr><tr><td>500</td><td>Dynamic Async</td><td>false</td><td>0</td><td>5</td><td>2,500</td><td>false</td><td>0.349</td></tr><tr><td>null</td><td></td><td>false</td><td></td><td></td><td></td><td>false</td><td></td></tr><tr><td>1000</td><td>Normal Sync</td><td>true</td><td>0</td><td>5</td><td>5,000</td><td>true</td><td>1.832</td></tr><tr><td>1000</td><td>Dynamic Sync</td><td>true</td><td>0</td><td>5</td><td>5,000</td><td>true</td><td>1.934</td></tr><tr><td>1000</td><td>Dynamic Async</td><td>false</td><td>0</td><td>5</td><td>5,000</td><td>false</td><td>0.508</td></tr></tbody></table>

<table><thead><tr><th width="108">Game Objects</th><th width="102">Event Types</th><th width="118" data-type="checkbox">Blocks UI / Gameplay</th><th width="67">For Loop</th><th>Classes (1 event per class)</th><th>Events Invoked</th><th width="151" data-type="checkbox">Modify UI, Game Objects, Call main thread</th><th>Seconds</th></tr></thead><tbody><tr><td>5,000</td><td>Normal Sync</td><td>true</td><td>0</td><td>1</td><td>5,000</td><td>true</td><td>1.622</td></tr><tr><td>5,000</td><td>Dynamic Sync</td><td>true</td><td>0</td><td>1</td><td>5,000</td><td>true</td><td>1.891</td></tr><tr><td>5,000</td><td>Dynamic Async</td><td>false</td><td>0</td><td>1</td><td>5,000</td><td>false</td><td>0.716</td></tr></tbody></table>

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 (<mark style="color:green;">If it makes sense</mark>) for better performance and to avoid blocking the main thread.&#x20;

For **Async games/apps** where most functionality is <mark style="color:blue;">async/multi-threaded</mark> consider using the **Unity Job System(**&#x77;ith DOT&#x53;**)** or combine <mark style="color:blue;">async Task/void</mark> methods with <mark style="color:blue;">Parallel.For / Parallel.ForEach</mark> when doing CPU intensive work. <mark style="color:blue;">Parallel.For / Parallel.ForEach</mark> will still block the **UI / main thread** if not used with <mark style="color:blue;">async void</mark> or <mark style="color:blue;">async Task</mark> methods. <mark style="color:green;">Do your own research if you don't know how to use</mark> <mark style="color:green;"></mark><mark style="color:green;">**asynchronous**</mark> <mark style="color:green;"></mark><mark style="color:green;">or</mark> <mark style="color:green;"></mark><mark style="color:green;">**Parallel**</mark> <mark style="color:green;"></mark><mark style="color:green;">programming</mark>. <mark style="color:red;">The advice I have mentioned above is not for all use cases and also considered bad in most contexts (like</mark> <mark style="color:blue;">async void</mark> <mark style="color:red;">or using async with</mark> <mark style="color:blue;">Parellel.ForEach</mark><mark style="color:red;">), especially outside of</mark> <mark style="color:red;"></mark><mark style="color:red;">**Unity Game Engine**</mark> where <mark style="color:red;"><mark style="color:blue;">async<mark style="color:blue;"></mark> is the standard for performant applications. Keep in mind <mark style="color:green;">Parallel programming</mark> <mark style="color:red;">(</mark>as well as <mark style="color:blue;">async</mark><mark style="color:red;">)</mark> 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
