Performance Test v2

After a few comments on the reddit, I've decided run a couple of more tests, using a few tips that I get from there, so the new results will be bellow, and first I want to make a few notes here:
 

  • I'm using Unity 4.5.5.1f with Visual Studio 2013
  • All of this are tested on my PC running on the editor, I'll make mobiles builds later
  • If you are looking to this, and thinking this can save the performance on your game, you're probably screwed! This at most of the cases will not BRING a big impact
  • There is a TON of other things to take in considerations besides how fast this performance.
  • Don't take this too serious :)
  • An amazing guy  braaadt show me this link [ http://mattwarren.org/2014/09/19/the-art-of-benchmarking/ ], and I've made a few more tests here, just to make an update and better version about it! 

Now for testing purposes I'll use this Main Method here: 

   1:  public void Profile(string pDescription, int pInteractions, bool pRemoveBestAndWorst, Action pAction)
   2:  {
   3:      //If I want to remove the best and the worst interaction
   4:      if (pRemoveBestAndWorst)
   5:          pInteractions += 2;
   6:   
   7:      //Cleaning the Memory
   8:      GC.Collect();
   9:      GC.WaitForPendingFinalizers();
  10:      GC.Collect();
  11:   
  12:          
  13:   
  14:      //Warm Up the method
  15:      pAction();
  16:      List<TimeSpan> tAllTimes = new List<TimeSpan>();
  17:      for (int i = 0 ; i < pInteractions ; i++)
  18:      {
  19:          Stopwatch tStopwatch = new Stopwatch();
  20:          tStopwatch.Start();
  21:   
  22:          pAction();
  23:   
  24:          tStopwatch.Stop();
  25:          tAllTimes.Add(tStopwatch.Elapsed);
  26:      }
  27:      TimeSpan tAverageTime = GetAverageTime(tAllTimes, pRemoveBestAndWorst);
  28:   
  29:      if (pRemoveBestAndWorst)
  30:          pInteractions -= 2;
  31:   
  32:      Debug.Log("Average time of " + pDescription + " in "+pInteractions+" times is: " + tAverageTime); 
  33:  }
  34:   
  35:  private TimeSpan GetAverageTime (List<TimeSpan> pListOfTimes, bool pRemoveBestAndWorst)
  36:  {
  37:      if (pRemoveBestAndWorst)
  38:      {
  39:          pListOfTimes.Sort((TimeSpan a, TimeSpan b) =>
  40:          {
  41:              b.Ticks.CompareTo(a.Ticks);
  42:              return 0;
  43:          });
  44:   
  45:          //Debug.Log("Removing worst: " + pListOfTimes[0]);
  46:          //Removing the fastest
  47:          pListOfTimes.RemoveAt(0);
  48:   
  49:          //Debug.Log("Removing best: " + pListOfTimes[pListOfTimes.Count - 1]);
  50:          //Removing the slowest
  51:          pListOfTimes.RemoveAt(pListOfTimes.Count - 1);
  52:   
  53:      }
  54:   
  55:   
  56:      double doubleAverageTicks = pListOfTimes.Average(timeSpan => timeSpan.Ticks);
  57:      long longAverageTicks = Convert.ToInt64(doubleAverageTicks);
  58:   
  59:      pListOfTimes.Sort((TimeSpan a, TimeSpan b) => b.CompareTo(a));
  60:   
  61:   
  62:   
  63:      return new TimeSpan(longAverageTicks);
  64:  }
  65:  private TimeSpan GetAverageTime (List<TimeSpan> pListOfTimes, bool pRemoveBestAndWorst)
  66:  {
  67:      if (pRemoveBestAndWorst)
  68:      {
  69:          pListOfTimes.Sort((TimeSpan a, TimeSpan b) =>
  70:          {
  71:              b.Ticks.CompareTo(a.Ticks);
  72:              return 0;
  73:          });
  74:   
  75:          //Debug.Log("Removing worst: " + pListOfTimes[0]);
  76:          //Removing the fastest
  77:          pListOfTimes.RemoveAt(0);
  78:   
  79:          //Debug.Log("Removing best: " + pListOfTimes[pListOfTimes.Count - 1]);
  80:          //Removing the slowest
  81:          pListOfTimes.RemoveAt(pListOfTimes.Count - 1);
  82:   
  83:      }
  84:   
  85:   
  86:      double doubleAverageTicks = pListOfTimes.Average(timeSpan => timeSpan.Ticks);
  87:      long longAverageTicks = Convert.ToInt64(doubleAverageTicks);
  88:   
  89:      pListOfTimes.Sort((TimeSpan a, TimeSpan b) => b.CompareTo(a));
  90:   
  91:   
  92:   
  93:      return new TimeSpan(longAverageTicks);
  94:  }

You can use the above code to test whatever you want, is not a TRUE PROFILER, but sometimes you just want make sure if something that you did, are faster than something before, maybe is useful to someone :)


So, lets do a couple of more tests and see what performance in each way:

Test 1 - An empty method

   1:  Profile("Empty Test", repeatForHowManyTimes, () =>
   2:              {
   3:                  for (int i = 0; i < totalItems; i++)
   4:                  {
   5:                      
   6:                  }
   7:              });

Average time of Method: 0.0000114


Test 2 - Populating an List<T>

   1:   Profile("Populate an List<T>", repeatForHowManyTimes, () =>
   2:              {
   3:                  List<ClassOne> tNewList = new List<ClassOne>();
   4:                  for (int j = 0 ; j < totalItems ; j++)
   5:                  {
   6:                      tNewList.Add(GenerateNewObject());
   7:                  }
   8:              });

Average time of Method: 0.0239758


Test 3 - Populating an Array

   1:  Profile("Populate an Array", repeatForHowManyTimes, () =>
   2:              {
   3:                  ClassOne[] tNewClass = new ClassOne[totalItems];
   4:                  for (int j = 0 ; j < totalItems ; j++)
   5:                  {
   6:                      tNewClass[j] = GenerateNewObject();
   7:                  }
   8:              });

Average time of Method: 0.0234332


Test 4 - Traveling a List<T> using foreach

   1:   List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:   
   7:  Profile("Foreach on a List<T>", repeatForHowManyTimes, () =>
   8:  {
   9:      foreach (ClassOne tClasseOne in tNewList)
  10:      {
  11:   
  12:      }
  13:  });

Average time of Method: 0.0001528


Test 5 - Traveling and Array using foreach

   1:  ClassOne[] tNewClass = new ClassOne[totalItems];
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewClass[j] = GenerateNewObject();
   5:  }
   6:  Profile("Foreach on a Array", repeatForHowManyTimes, () =>
   7:  {
   8:      foreach (ClassOne tClasseOne in tNewClass)
   9:      {
  10:   
  11:      }
  12:  });
  13:              

Average time of Method: 0.0000192


Test 6 - Traveling a List<T> with the Count on the for

   1:  List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:  Profile("Traveling a List without asgin the Count", repeatForHowManyTimes, () =>
   7:  {
   8:      for (int index = 0 ; index < tNewList.Count ; index++)
   9:      {
  10:          ClassOne tClasseOne = tNewList[index];
  11:      }
  12:  });

Average time of Method: 0.0000658


Test 7 - Traveling a List<T> using for

   1:  List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:  Profile("Traveling a List<T> using For", repeatForHowManyTimes, () =>
   7:  {
   8:      int tCount = tNewList.Count;
   9:      for (int index = 0 ; index < tCount ; index++)
  10:      {
  11:          ClassOne tClasseOne = tNewList[index];
  12:      }
  13:  });

Average time of Method: 0.0000423


Teste 8 - Traveling an Array using For

   1:  ClassOne[] tNewClass = new ClassOne[totalItems];
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewClass[j] = GenerateNewObject();
   5:  }
   6:  Profile("Traveling a Array using For", repeatForHowManyTimes, removeBestAndWorst, () =>
   7:  {
   8:      int tCount = tNewClass.Length;
   9:      for (int index = 0 ; index < tCount ; index++)
  10:      {
  11:          ClassOne tClasseOne = tNewClass[index];
  12:      }
  13:  });

Average time of Method: 0.0000205


Test 9 - Traveling a List<T> testing for some case

   1:  List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:  Profile("Seraching on a Array interating a List<T>", repeatForHowManyTimes, removeBestAndWorst, () =>
   7:  {
   8:      int tCount = tNewList.Count;
   9:      int tRandomItemToSerach = Random.Range(0, 100);
  10:   
  11:      for (int index = 0 ; index < tCount ; index++)
  12:      {
  13:          ClassOne tClasseOne = tNewList[index];
  14:          if (tClasseOne.simpleInt == tRandomItemToSerach)
  15:          {
  16:   
  17:          }
  18:      }
  19:  });

Average time of Method: 0.0000572


Test 10 - Traveling a Array testing for some case

   1:  ClassOne[] tNewClass = new ClassOne[totalItems];
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewClass[j] = GenerateNewObject();
   5:  }
   6:  Profile("Seraching on a Array interating for", repeatForHowManyTimes, removeBestAndWorst, () =>
   7:  {
   8:      int tCount = tNewClass.Length;
   9:      int tRandomItemToSerach = Random.Range(0, 100);
  10:   
  11:      for (int index = 0 ; index < tCount ; index++)
  12:      {
  13:          ClassOne tClasseOne = tNewClass[index];
  14:          if (tClasseOne.simpleInt == tRandomItemToSerach)
  15:          {
  16:   
  17:          }
  18:      }
  19:  });

Average time of Method: 0.0000354


Test 11 - Searching on a List<T> using LinQ

   1:  List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:  Profile("Search on a List<T> using LinQ", repeatForHowManyTimes, removeBestAndWorst, () =>
   7:  {
   8:      int tRandomItemToSerach = Random.Range(0, 100);
   9:      ClassOne tFound = tNewList.FirstOrDefault(x => tRandomItemToSerach == x.simpleInt);
  10:  });

Average time of Method: 0.0000178


Test 12 - Inverted for testing case 

   1:  List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:   
   7:  Profile("Inverted Interaction on a List<T>", repeatForHowManyTimes, removeBestAndWorst, () =>
   8:  {
   9:      int tCount = tNewList.Count;
  10:      int tRandomItemToSerach = Random.Range(0, 100);
  11:   
  12:      for (int index = tCount - 1 ; index >= 0 ; index--)
  13:      {
  14:          ClassOne tClasseOne = tNewList[index];
  15:          if (tClasseOne.simpleInt == tRandomItemToSerach)
  16:          {
  17:   
  18:          }
  19:      }
  20:  });

Average time of Method: 0.0000554


Compatarions

Populating a List<T> vs Array

Screenshot_103014_035535_PM.jpg

Traveling using foreach List<T> vs Array

Screenshot_103014_035837_PM.jpg

Traveling a list with the Count as enumerator vs assigned to a variable

Screenshot_103014_040153_PM.jpg

Traveling using for Array vs List<T>

Comparison inside a for List<T> vs Array

Using LinQ vs regular comparision

Regular For vs Inverted For

Last conclusions :)

  •  User regular array if you can, I know sometimes you need to change this size a lot, in this case go with List<T>
  • Assign the variable to a for, (int tCount = tList.Count) bofore the for, boost the performance in more than 55%
  • Using LinQ when you can, is really fast, in my tests about 221% faster.
  • And inverted for, (i--) is 23% slower :P
  • Using a foreach in a Array compared to a list, is 695% faster :P