Search Unity

Some comparison results testing '==', CompareTag(), Equals() GetComponent(), ScriptableObject tags

Discussion in 'General Discussion' started by MostHated, Jul 28, 2020.

  1. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    During my normal perusal of GitHub, I came across a repo for a ScriptableObject based (multi)tagging system (link at bottom) that sounded like it might be useful, so I decided to check it out since I was building my waypoint system tooling and found myself using several different ways to determine what was clicked on, what the mouse was over, what type of waypoint this or that was, etc through comparisons using tags, GetComponent calls, etc.

    I had seen/heard of multi-tagging systems before but never really thought much of it until now. It is well known that using gameObject.tag == "TagName"; is relatively slow, but it got me wondering just how much of a difference there is between the different types of comparisons I was using in my tooling.

    I was mostly using a mix of go.CompareTag() and go.GetComponent<>().someValue == somethingElse;
    I was surprised to see that when I was testing using the GetComponent method, it was actually slower than using CompareTag.

    The real juiciness, though, was that using ScriptableObject tags, it was about twice as fast or more as the other methods.

    The test was ran with 1000 active GameObject waypoints in scene, then each test ran for 100 iterations, which was the repeated 10 times.
    I tried to test in a manner that might resemble something that someone might actually do.
    Results are ElapsedMilliseconds / 1000f and ElapsedTicks / 100f.

    Disclaimer - I am no expert at testing, so if any of this seems like an unfair/incorrect test, let me know. I just tested the things I used and wanted to see how they stacked up. Your milage may vary.

    Code (CSharp):
    1. ElapsedMilliseconds / 1000f and ElapsedTicks / 100f.
    2. Test1: Comparison using: go.tag == "value";
    3. Test2: Comparison using: go.CompareTag("Value");
    4. Test3: Comparison using: go.GetComponent<>().Equals(value);
    5. Test4: Comparison using: go.GetComponent<>().field == value;
    6. Test5: Comparison using: ScriptableObject: go.HasTag(tagObject);
    7.  
    8. [Log] [12:26:02]
    9. Test1 time: 0.041ms (4116.13 ticks) Count: 100000
    10. Test2 time: 0.022ms (2216.14 ticks) Count: 100000
    11. Test3 time: 0.025ms (2581.18 ticks) Count: 100000
    12. Test4 time: 0.025ms (2552.7  ticks) Count: 100000
    13. Test5 time: 0.011ms (1110.6  ticks) Count: 100000
    Here are the results of 10 runs if anyone cares to see it.
    Code (CSharp):
    1. Test1: Comparison using: go.tag == "value";
    2. Test2: Comparison using: go.CompareTag("Value");
    3. Test3: Comparison using: go.GetComponent<>().Equals(value);
    4. Test4: Comparison using: go.GetComponent<>().field == value;
    5. Test5: Comparison using: ScriptableObject: go.HasTag(tagObject);
    6.  
    7. [Log] [12:26:02]
    8. Test1 time: 0.041ms (4116.13 ticks) Count: 100000
    9. Test2 time: 0.022ms (2216.14 ticks) Count: 100000
    10. Test3 time: 0.025ms (2581.18 ticks) Count: 100000
    11. Test4 time: 0.025ms (2552.7  ticks) Count: 100000
    12. Test5 time: 0.011ms (1110.6  ticks) Count: 100000
    13.  
    14. [Log] [12:26:02]
    15. Test1 time: 0.038ms (3899.63 ticks) Count: 100000
    16. Test2 time: 0.023ms (2358.48 ticks) Count: 100000
    17. Test3 time: 0.024ms (2495.23 ticks) Count: 100000
    18. Test4 time: 0.025ms (2510.68 ticks) Count: 100000
    19. Test5 time: 0.011ms (1149.9  ticks) Count: 100000
    20.  
    21. [Log] [12:26:02]
    22. Test1 time: 0.04ms  (4001.37 ticks) Count: 100000
    23. Test2 time: 0.023ms (2399.91 ticks) Count: 100000
    24. Test3 time: 0.025ms (2528.8  ticks) Count: 100000
    25. Test4 time: 0.025ms (2575.85 ticks) Count: 100000
    26. Test5 time: 0.011ms (1178.48 ticks) Count: 100000
    27.  
    28. [Log] [12:26:03]
    29. Test1 time: 0.038ms (3854.92 ticks) Count: 100000
    30. Test2 time: 0.022ms (2298.34 ticks) Count: 100000
    31. Test3 time: 0.025ms (2506.11 ticks) Count: 100000
    32. Test4 time: 0.025ms (2529.03 ticks) Count: 100000
    33. Test5 time: 0.012ms (1242.61 ticks) Count: 100000
    34.  
    35. [Log] [12:26:03]
    36. Test1 time: 0.039ms (3937.45 ticks) Count: 100000
    37. Test2 time: 0.023ms (2311.56 ticks) Count: 100000
    38. Test3 time: 0.025ms (2503.31 ticks) Count: 100000
    39. Test4 time: 0.024ms (2463.07 ticks) Count: 100000
    40. Test5 time: 0.011ms (1142.34 ticks) Count: 100000
    41.  
    42. [Log] [12:26:03]
    43. Test1 time: 0.039ms (3926.93 ticks) Count: 100000
    44. Test2 time: 0.023ms (2359.79 ticks) Count: 100000
    45. Test3 time: 0.025ms (2512.77 ticks) Count: 100000
    46. Test4 time: 0.025ms (2570.87 ticks) Count: 100000
    47. Test5 time: 0.012ms (1207.86 ticks) Count: 100000
    48.  
    49. [Log] [12:26:03]
    50. Test1 time: 0.038ms (3804.8  ticks) Count: 100000
    51. Test2 time: 0.022ms (2292.1  ticks) Count: 100000
    52. Test3 time: 0.025ms (2566.21 ticks) Count: 100000
    53. Test4 time: 0.026ms (2638.39 ticks) Count: 100000
    54. Test5 time: 0.011ms (1182.4  ticks) Count: 100000
    55.  
    56. [Log] [12:26:03]
    57. Test1 time: 0.04ms  (4085.26 ticks) Count: 100000
    58. Test2 time: 0.023ms (2376.73 ticks) Count: 100000
    59. Test3 time: 0.024ms (2492.07 ticks) Count: 100000
    60. Test4 time: 0.025ms (2594.44 ticks) Count: 100000
    61. Test5 time: 0.012ms (1212.78 ticks) Count: 100000
    62.  
    63. [Log] [12:26:03]
    64. Test1 time: 0.038ms (3871.27 ticks) Count: 100000
    65. Test2 time: 0.023ms (2300.9  ticks) Count: 100000
    66. Test3 time: 0.024ms (2481.47 ticks) Count: 100000
    67. Test4 time: 0.025ms (2536.15 ticks) Count: 100000
    68. Test5 time: 0.011ms (1194.89 ticks) Count: 100000
    69.  
    70. [Log] [12:26:03]
    71. Test1 time: 0.037ms (3779.44 ticks) Count: 100000
    72. Test2 time: 0.023ms (2311.07 ticks) Count: 100000
    73. Test3 time: 0.025ms (2531.11 ticks) Count: 100000
    74. Test4 time: 0.025ms (2540.28 ticks) Count: 100000
    75. Test5 time: 0.012ms (1230.25 ticks) Count: 100000

    Here was the code used to run the tests

    Code (CSharp):
    1. [Button("Run Performance Test")]
    2. public void PerformanceGauge()
    3. {
    4.     for (int t = 0; t < 10; t++)
    5.     {
    6.         // ------------------------------------------------------------------------------ Test1
    7.         // -- Waypoint Test1 Comparison using '==' --------------------------------------------
    8.         var test1Tagged = new Dictionary<GameObject, bool>();
    9.         var test1Count = 0;
    10.         var test1Stopwatch = new Stopwatch();
    11.         test1Stopwatch.Start();
    12.         for (int w = 0; w < 100; w++)
    13.         {
    14.             for (int i = 0; i < waypointList.Count; i++)
    15.             {
    16.                 test1Tagged.Add(waypointList[i].gameObject, waypointList[i].gameObject.tag == "Waypoint");
    17.                 test1Count++;
    18.             }
    19.  
    20.             test1Tagged.Clear();
    21.         }
    22.  
    23.         test1Stopwatch.Stop();
    24.  
    25.         // ------------------------------------------------------------------------------ Test2
    26.         // -- Waypoint Test2 Comparison using .CompareTag() -----------------------------------
    27.         var test2Tagged = new Dictionary<GameObject, bool>();
    28.         var test2Count = 0;
    29.         var test2Stopwatch = new Stopwatch();
    30.         test2Stopwatch.Start();
    31.         for (int w = 0; w < 100; w++)
    32.         {
    33.             for (int i = 0; i < waypointList.Count; i++)
    34.             {
    35.                 test2Tagged.Add(waypointList[i].gameObject, waypointList[i].gameObject.CompareTag("Waypoint"));
    36.                 test2Count++;
    37.             }
    38.  
    39.             test2Tagged.Clear();
    40.         }
    41.  
    42.         test2Stopwatch.Stop();
    43.  
    44.         // ------------------------------------------------------------------------------ Test3
    45.         // -- Waypoint Test3 Comparison using GetComponent<>().Equals -------------------------
    46.         var test3Tagged = new Dictionary<GameObject, bool>();
    47.         var test3Count = 0;
    48.         var test3Stopwatch = new Stopwatch();
    49.         test3Stopwatch.Start();
    50.  
    51.         for (int w = 0; w < 100; w++)
    52.         {
    53.             for (int i = 0; i < waypointList.Count; i++)
    54.             {
    55.                 test3Tagged.Add(waypointList[i].gameObject, waypointList[i].gameObject.GetComponent<Waypoint>().waypointID.Equals(i));
    56.                 test3Count++;
    57.             }
    58.  
    59.             test3Tagged.Clear();
    60.         }
    61.  
    62.         test3Stopwatch.Stop();
    63.  
    64.         // ------------------------------------------------------------------------------ Test4
    65.         // -- Waypoint Test4 Comparison using GetComponent<>().field --------------------------
    66.         var test4Tagged = new Dictionary<GameObject, bool>();
    67.         var test4Count = 0;
    68.         var test4Stopwatch = new Stopwatch();
    69.         test4Stopwatch.Start();
    70.  
    71.         for (int w = 0; w < 100; w++)
    72.         {
    73.             for (int i = 0; i < waypointList.Count; i++)
    74.             {
    75.                 test4Tagged.Add(waypointList[i].gameObject, waypointList[i].gameObject.GetComponent<Waypoint>().waypointID == i);
    76.                 test4Count++;
    77.             }
    78.  
    79.             test4Tagged.Clear();
    80.         }
    81.  
    82.         test4Stopwatch.Stop();
    83.  
    84.         // ------------------------------------------------------------------------------ Test5
    85.         // -- Waypoint Test5 Comparison using ScriptableObject Tagging ------------------------
    86.         var test5Tagged = new Dictionary<GameObject, bool>();
    87.         var test5Count = 0;
    88.         var test5Stopwatch = new Stopwatch();
    89.         test5Stopwatch.Start();
    90.  
    91.         for (int w = 0; w < 100; w++)
    92.         {
    93.             for (int i = 0; i < waypointList.Count; i++)
    94.             {
    95.                 test5Tagged.Add(waypointList[i].gameObject, waypointList[i].gameObject.HasTag(TagManager.waypointTag));
    96.                 test5Count++;
    97.             }
    98.  
    99.             test5Tagged.Clear();
    100.         }
    101.  
    102.         test5Stopwatch.Stop();
    103.  
    104.         var output =
    105.             $"Test1 time: {test1Stopwatch.ElapsedMilliseconds / 1000f}ms ({test1Stopwatch.ElapsedTicks / 100f} ticks) Count: {test1Count}\n" +
    106.             $"Test2 time: {test2Stopwatch.ElapsedMilliseconds / 1000f}ms ({test2Stopwatch.ElapsedTicks / 100f} ticks) Count: {test2Count}\n" +
    107.             $"Test3 time: {test3Stopwatch.ElapsedMilliseconds / 1000f}ms ({test3Stopwatch.ElapsedTicks / 100f} ticks) Count: {test3Count}\n" +
    108.             $"Test4 time: {test4Stopwatch.ElapsedMilliseconds / 1000f}ms ({test4Stopwatch.ElapsedTicks / 100f} ticks) Count: {test4Count}\n" +
    109.             $"Test5 time: {test5Stopwatch.ElapsedMilliseconds / 1000f}ms ({test5Stopwatch.ElapsedTicks / 100f} ticks) Count: {test5Count}\n";
    110.  
    111.         Debug.Log(output);
    112.     }
    113. }

    Gist of the above stuff.


    If anyone is interested, here is the package I used to test. I have been messing with it all day and between the fact that you can have as many tags as you want (it seems like you can sort of make groupings similar to ArchetypeGroups in ECS?) and the fact that it is twice as fast, I am digging it.

    https://github.com/IntoTheDev/MultiTag-System-for-Unity

    Thanks,
    -MH
     
    Last edited: Jul 28, 2020