Search Unity

C# Optimize Gui Manager script

Discussion in 'Scripting' started by clearrose, Oct 13, 2015.

  1. clearrose

    clearrose

    Joined:
    Oct 24, 2012
    Posts:
    349
    Our Gui script is causing a good amount of Garbage Collection in it's Update (), but I'm not sure exactly how I would approach optimizing the script. I think that the garbage collection is being triggered because the constant update of the text displaying the amount of collectables, and the distance the player has traveled in every update cycle. We want these features, so need to optimize and not delete.

    Also, can the type of front being used affect performance ?

    Profiler:



    Gui Manager update:

    Code (CSharp):
    1. void Update()
    2.     {
    3.  
    4.         //If the game is in play mode
    5.         if (inPlayMode)
    6.         {
    7.             //Update the main UI's status texts
    8.             coinText.text = AddDigitDisplay(collectedCoins, 4);
    9.             distanceText.text = AddDigitDisplay(distanceTraveled, 5);
    10.         }
    11.     }
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    I think we'd need to see AddDigitDisplay to help you. Building strings is an extremely common source of GC cruft, especially if you use a lot of +'s.
     
  3. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    Do you need to Update it every frame? Try using a coroutine to update it less frequently. Maybe every 0.5 or even 1 second or something. Or if the data is only changed due to a game event, you should use delegates to only fire the UI update when a value changes.
     
  4. clearrose

    clearrose

    Joined:
    Oct 24, 2012
    Posts:
    349
    Oops sorry about that:

    Code (CSharp):
    1.  private string AddDigitDisplay(int number, int digits)
    2.     {
    3.         string s = "";
    4.  
    5.         for (int i = 0; i < digits - number.ToString().Length; i++)
    6.             s += "0";
    7.  
    8.         s += number.ToString();
    9.  
    10.         return s;
    11.     }
    I could try a coroutine, I'm wondering though would ether Late Update () or Fixed Update () work as well? Just brainstorming some ideas?
     
  5. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    I had similar issues with string handling in my game too. I thought StringBuilder would fix it but whenever you convert it to a string, the version of Mono Unity uses generates garbage anyway so it doesn't help.

    I ended up with two solutions : My timers and distance things that need to be updated regularly have a couple of optimizations:
    • Updated in a coroutine once a second - this might slow down the UI responsiveness but it really helps (generates garbage once a second not 60 times) it looks ok as well. Of course if the game ends or the player dies I quickly do one last UI update to show the actual final distance score. Players don't usually die on the exact second!
    • This might not work for your game but I try and have semi intelligent updates - only update the distance if the distance actually changed by a reasonable amount.
    For other things like score, where the score only changes when something happens I have a simple event system. The UI listens for score changes and Updates the score text when something changes.
     
  6. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Your garbage collection issue has zero to do with where you're calling this code from, and everything to do with what this code is. This function could be a textbook example of how to generate as much GC as possible while putting together a string. Not only do you use += on strings as much as possible (each instance of which generates garbage), but in your for loop you use number.ToString() to calculate the length....and each of those calls will generate garbage. It's also really slow for what it is. (edit: I don't mean to have a scolding tone in this, even though I see that's how it came off.... apologies)

    If you want to avoid garbage collection and you have to build strings, it is vital to learn how to use string.Format and int.ToString("stuff") functions. These functions are efficiently coded to clean up after themselves and not leave behind anything for the GC. As long as you avoid any concatenation (string + string), and minimize your .ToString() calls as much as you can, you should be able to drastically reduce the amount of garbage you create.

    In this case, there's actually a int.ToString parameter you can use to replace this entire function! Here's a page showing how to use it.
     
    Korno likes this.
  7. clearrose

    clearrose

    Joined:
    Oct 24, 2012
    Posts:
    349
    No, worries I'm modifying code from the asset store. This was the way it was originally, so i guess I'm cleaning up after someone else.

    ( Also Note: I'm a part of a small indie team where we are all trained as artists and not programmers. I have the most experience with code, and i'm just a beginner. )

    So here's my attempt:

    Code (CSharp):
    1.  private string AddDigitDisplay(int number, int digits)
    2.     {
    3.         /*string s = "";
    4.  
    5.         for (int i = 0; i < digits - number.ToString().Length; i++)
    6.             s += "0";
    7.  
    8.         s += number.ToString();
    9.  
    10.         return s;*/
    11.  
    12.         string s = "";
    13.  
    14.         s.PadLeft (s.Length + 4, '0');
    15.     }
    How would I use int number and int digits, in a new string with the examples given? do I have too?

    and I'm getting this error:

     
  8. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    Try to make to it return int instead of string, and then use string format, strings are arrays and expensive
     
  9. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
  10. clearrose

    clearrose

    Joined:
    Oct 24, 2012
    Posts:
    349
    I did try to return int just as you suggested but I got this error:



    Current code:

    Code (CSharp):
    1. //Converts a number to a string, with a given digit number. For example, this turns 4 to "0004"
    2.     private string AddDigitDisplay(int number, int digits)
    3.     {
    4.         /*string s = "";
    5.  
    6.         for (int i = 0; i < digits - number.ToString().Length; i++)
    7.             s += "0";
    8.  
    9.         s += number.ToString();
    10.  
    11.         return s;*/
    12.  
    13.          return string.Format ("{0:d4}{1:d4}", number, digits);
    14.  
    15.         //s.PadLeft (s.Length + 4, '0');
    16.     }
    17.  
    This did work but there was two problems with this: one, the distance was calculated as seconds instead of a measure of distance. Two, the garbage collection was even worse then before.

     
  11. clearrose

    clearrose

    Joined:
    Oct 24, 2012
    Posts:
    349
    While I just tried this:

    Code (CSharp):
    1. void Update()
    2.     {
    3.  
    4.         //If the game is in play mode
    5.         if (inPlayMode)
    6.         {
    7.             //Update the main UI's status texts
    8.             coinText.text = collectedCoins.ToString("0000");
    9.             distanceText.text = distanceTraveled.ToString("00000");
    10.  
    11.            // coinText.text = AddDigitDisplay(collectedCoins, 4);
    12.           //  distanceText.text = AddDigitDisplay(distanceTraveled, 5);
    13.         }
    14.     }
    It did work, but the Garbage was way worse with the changes:

     
  12. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    I meant something like this,
    Code (CSharp):
    1.     private int AddDigitDisplay(int number, int digits)
    2.     {
    3.         int s = 0;
    4.         for (int i = 0; i < digits - number; i++)          
    5.         s += number;
    6.         return s;
    7.     }
    Then use stringformat to show the numbers output in the format you prefer, followed by the text you want to add with that. Btw you don't need to show this numbers every frame, you should lower it with every second or every half second.
     
  13. clearrose

    clearrose

    Joined:
    Oct 24, 2012
    Posts:
    349
    Still got an error:

    Code (CSharp):
    1.  private string AddDigitDisplay(int number, int digits)
    2.     {
    3.         int MyStringInt = 0;
    4.  
    5.         for (int i = 0; i < digits - number; i++)
    6.         MyStringInt += number;
    7.  
    8.         return MyStringInt;
    9.  
    10.         /*string s = "";
    11.  
    12.         for (int i = 0; i < digits - number.ToString().Length; i++)
    13.             s += "0";
    14.  
    15.         s += number.ToString();
    16.  
    17.         return s;*/
    18.  
    19.         //return string.Format ("{0:d4}{1:d4}", number, digits);
    20.  
    21.         //s.PadLeft (s.Length + 4, '0');
    22.     }
     
  14. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    there is no error or string in the code i show you, but its the way you use it.
     
  15. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You should also consider making coins a property. Only change the UI when it actually updates.
     
    McMayhem likes this.
  16. clearrose

    clearrose

    Joined:
    Oct 24, 2012
    Posts:
    349
    Well, we fixed the problem by just starting fresh and commenting out the old code from the GUI manager.

    Edit: almost forget this.


    This unity video helped by giving a good template to start fresh from:

    https://unity3d.com/learn/tutorials/projects/space-shooter/counting-points
     
  17. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    If you use IEnumerator for counting int, you get overhead overtime

    Code (CSharp):
    1.  
    2.  
    3. private int DigitDisplay(int number, int digits)
    4.   {
    5.   int s = 0;
    6.   for (int i = 0; i < digits - number; i++)
    7.   s += number;
    8.   return s;
    9.   }
    10.  
    11.   public void ScoreScreen()
    12.   {
    13.   coinText.text    =  string.Format ("{0:d4}", DigitDisplay(Inputnumber, Inputdigits));
    14.   }
    15.  
    16.  
    17.  
    18.  
    Then you do what BoredMoron recommended.

    I haven't tested the code, but it should work, and shows you what i mean is a good solution in your case.
     
    Last edited: Oct 16, 2015